
翻蚳è
ããïŒã
ãšãªã¯ãµãŒãšãã§ããã¯ã¹ã¯ãææ°ã®ãŠã§ãéçºãã©ãã«é²ãã§ãããã®è¯ãäŸã§ãã ãã§ã«ãããã®ããŒã«ã¯ãWebã¢ããªã±ãŒã·ã§ã³ã®ãªã¢ã«ã¿ã€ã ãã¯ãããžãŒãžã®è³ªã®é«ãã¢ã¯ã»ã¹ãæäŸããŸãã 察話æ§ãåäžãããµã€ãããã«ããŠãŒã¶ãŒãã©ãŠã¶ãŒã²ãŒã ããã€ã¯ããµãŒãã¹ã¯ããããã®æè¡ãããŸãæ©èœããåéã§ãã 以äžã¯ãPhoenixãã¬ãŒã ã¯ãŒã¯ã§ã®éçºã®è©³çŽ°ãªåŽé¢ã説æããäžé£ã®11ã®èšäºã®ç¿»èš³ã§ããããã°ãšã³ãžã³ã®ãããªäºçŽ°ãªããšã®ããã«æããŸãã ããããæ¥ãã§ã€ãŸãããªãã§ãã ãããç¹ã«èšäºããšãªã¯ãµãŒã«æ³šæãæããã圌ã®ãã©ãã¯ãŒã«ãªãããã«ä¿ãå Žåãããã¯æ¬åœã«é¢çœãã§ãããã
ãã®ããŒãã§ã¯ãããã°ã®åºç€ãå®æããããã¹ããããã«æãäžããæçµçã«æ¿èªãè¿œå ããŸãã å°ãé
ããŠç³ãèš³ãããŸããããæ確ãªã¹ã±ãžã¥ãŒã«ãå³å®ããããã¹ã±ãžã¥ãŒã«ãæ©ããŸãã ã
çŸæç¹ã§ã¯ãã¢ããªã±ãŒã·ã§ã³ã¯ä»¥äžã«åºã¥ããŠããŸãã
- ãšãªã¯ãµãŒ ïŒv1.3.1
- ãã§ããã¯ã¹ ïŒv1.2.0
- EctoïŒv2.0.2
- ã³ã¡ãã³ ïŒv2.5.2
ããã€ãã®ãã°ãä¿®æ£ããŸã
æåã®éšåãå®è¡ããå ŽåãElixir / Phoenixã§åäœããããã°ãšã³ãžã³ã
ããçšåºŠæ©èœããã¯ãã§ãã ããªããç§ã®ãããªäººã§ããã°ããã®ãããªäžèŠå°ããªä»äºã§ãè奮ããããéãåé²ããããã«ãªããã³ãŒããããã«ç£šããããšãã欲æ±ãåŒãèµ·ãããŸãã
äœæ¥ã®é²æç¶æ³ã远跡ããå Žåã¯ã
Githubã®ãªããžããªã«ãã¹ãŠã®ã³ãŒããã¢ããããŒãããŸããã
æåã®ãã°ã¯ã
httpïŒ// localhostïŒ4000 / sessions / newã®ã¢ãã¬ã¹ã«ç§»åãã[
éä¿¡ ]ãã¿ã³ãã¯ãªãã¯ããããšã§ç°¡åã«åçŸã§ããŸãã 次ã®ãããªãšã©ãŒã¡ãã»ãŒãžã衚瀺ãããŸãã
nil given for :username, comparison with nil is forbidden as it always evaluates to false. Pass a full query expression and use is_nil/1 instead.
SessionControllerã® createé¢æ°ãèŠããšãäœãåé¡ãªã®ãããã«ããããŸãã
def create(conn, %{"user" => user_params}) do user = Repo.get_by(User, username: user_params["username"]) user |> sign_in(user_params["password"], conn) end
ãã®ããã
ãŠãŒã¶ãŒåã®ä»£ããã«ç©ºã®å€ãå«ãïŒãŸãã¯äœãå«ãŸãªãïŒæååããã©ã¡ãŒã¿ãŒã§éä¿¡ãããšããšã©ãŒãçºçããŸãã ããã«ä¿®æ£ããŸãããã 幞ããªããšã«ãããã¯ã
guardç¯ãš
ãã¿ãŒã³ãããã³ã°ã䜿çšããŠç°¡åã«å®è¡ã§ããŸãã çŸåšã®
äœæé¢æ°ã次ã®ãã®ã«çœ®ãæããŸãã
def create(conn, %{"user" => %{"username" => username, "password" => password}}) when not is_nil(username) and not is_nil(password) do user = Repo.get_by(User, username: username) sign_in(user, password, conn) end def create(conn, _) do failed_login(conn) end
2çªç®ã®
äœæé¢æ°ã®
paramsåŒæ°ãã¢ã³ããŒã¹ã³ã¢ã«çœ®ãæããŸããã©ãã§ã䜿çšããå¿
èŠããªãããã§ãã ãŸãã
failed_loginé¢æ°ãåç
§ããŸããããã¯ãã©ã€ããŒããšããŠè¿œå ããå¿
èŠããããŸãã
web / controllers / session_controller.exãã¡ã€ã«ã§
ãComeoninã€ã³ããŒãã
å€æŽããŸãã
import Comeonin.Bcrypt, only: [checkpw: 2, dummy_checkpw: 0]
誰ããŠãŒã¶ãŒãåæããã ãã§ã¯ã¿ã€ã ã¢ã¿ãã¯ãéå§ã§ããªãããã«ã
dummy_checkpwïŒïŒãåŒã³åºãå¿
èŠããããŸãã 次ã«ã
failed_loginé¢æ°ãè¿œå ããŸãã
defp failed_login(conn) do dummy_checkpw() conn |> put_session(:current_user, nil) |> put_flash(:error, "Invalid username/password combination!") |> redirect(to: page_path(conn, :index)) |> halt() end
ç¹°ãè¿ããŸãããäžéšã®
dummy_checkpwïŒïŒã®åŒã³åºãã«æ³šæããŠ
ãã ãã ïŒ ãŸãã
current_userã»ãã·ã§ã³ãã¯ãªã¢ãããŠãŒã¶ãŒã«ééã£ããã°ã€ã³ãšãã¹ã¯ãŒããç¥ããããã©ãã·ã¥ã¡ãã»ãŒãžãèšå®ããã¡ã€ã³ããŒãžã«ãªãã€ã¬ã¯ãããŸãã æåŸã«ã
halté¢æ°ãåŒã³åºããŸããããã¯ãããã«ã¬ã³ããªã³ã°ã®åé¡ã«å¯Ÿããåççãªé²åŸ¡ã§ãã ãããŠããã¹ãŠã®åæ§ã®ã³ãŒããæ°ããé¢æ°ã®åŒã³åºãã«çœ®ãæããŸãã
defp sign_in(user, _password, conn) when is_nil(user) do failed_login(conn) end defp sign_in(user, password, conn) do if checkpw(password, user.password_digest) do conn |> put_session(:current_user, %{id: user.id, username: user.username}) |> put_flash(:info, "Sign in successful!") |> redirect(to: page_path(conn, :index)) else failed_login(conn) end end
ãããã®ç·šéã¯ãæ¢åã®ãã¹ãŠã®å¥åŠãªãã°ã€ã³ãã°ãåŠçããå¿
èŠããããŸããããã«ãããè¿œå ãããŠãŒã¶ãŒã«æçš¿ãé¢é£ä»ããããšãã§ããŸãã
移è¡ãè¿œå
æåã«ã
æçš¿ããŒãã«ã®
ãŠãŒã¶ãŒããŒãã«ãžã®ãªã³ã¯ãè¿œå ããŸãã ãããè¡ãã«ã¯ã
ecto-generatorã䜿çšããŠã移è¡ãäœæããŸãã
$ mix ecto.gen.migration add_user_id_to_posts
çµè«ïŒ
Compiling 1 file (.ex) * creating priv/repo/migrations * creating priv/repo/migrations/20160720211140_add_user_id_to_posts.exs
äœæãããã¡ã€ã«ãéããšããã®äžã«ã¯äœã衚瀺ãããŸããã ãããã£ãŠã次ã®ã³ãŒãã
å€æŽé¢æ°ã«è¿œå ããŸãã
def change do alter table(:posts) do add :user_id, references(:users) end create index(:posts, [:user_id]) end
ããã«ããããŠãŒã¶ãŒããŒãã«ã
åç
§ãã
user_idåãšãã®ã€ã³ããã¯ã¹ãè¿œå ãã
ãŸã ã
mix ecto.migrate
ãå®è¡ããã¢ãã«ã®ç·šéãéå§ããŸãã
æçš¿ãšãŠãŒã¶ãŒãçµã³ä»ããŸã
web / models / post.exãã¡ã€ã«ãéããŠã
ãŠãŒã¶ãŒã¢ãã«ãžã®ãªã³ã¯ãè¿œå ããŸãããã
æçš¿ã¹ããŒã å
ã«ã次ã®è¡ãé
眮ããŸãã
belongs_to :user, Pxblog.User
Postã¢ãã«ãæããã£ãŒãããã¯ã
Userã¢ãã«ã«è¿œå ããå¿
èŠããããŸãã
web / models / user.exãã¡ã€ã«ã®
usersã¹ããŒãå
ã«ã次ã®è¡ãé
眮ããŸãã
has_many :posts, Pxblog.Post
ãŸãã
æçš¿ã³ã³ãããŒã©ãŒãéããæçš¿ããŠãŒã¶ãŒã«çŽæ¥é¢é£ä»ããå¿
èŠããããŸãã
æ¹æ³ãå€ãã
ãŠãŒã¶ãŒå
ã®æçš¿ãæå®ããŠã«ãŒã¿ãŒãæŽæ°ããããšããå§ããŸãããã ãããè¡ãã«ã¯ã
web / router.exãã¡ã€ã«ãéãããã¹
/ãŠãŒã¶ãŒãš
/æçš¿ã次ã®
ããã«çœ®ãæã
ãŸã ã
resources "/users", UserController do resources "/posts", PostController end
ã³ã³ãããŒã©ãŒãä¿®æ£ããŸã
mix phoenix.routes
ãä»ããå®è¡ããããšãããšããšã©ãŒãçºçããŸãã ãããæšæºã§ãïŒ ãã¹ã®æ§é ãå€æŽããããã
post_pathãã«ããŒã¯å€±ãããŸããããã®æ°ããããŒãžã§ã³ã¯
user_post_pathãšåŒã°ããæ¥ç¶ããããªãœãŒã¹ãåç
§ããŸãã ãã¹ãããããã«ããŒã䜿çšãããšãå¥ã®ãªãœãŒã¹ãå¿
èŠãšãããªãœãŒã¹ïŒæçš¿ã«ãŠãŒã¶ãŒãå¿
èŠãªã©ïŒã§è¡šããããã¹ã«ã¢ã¯ã»ã¹ã§ããŸãã
ãããã£ãŠãéåžžã®
post_pathãã«ããŒãããå Žåã次ã®ããã«åŒã³åºããŸãã
post_path(conn, :show, post)
connãªããžã§ã¯ãã¯æ¥ç¶ãªããžã§ã¯ããatom
ïŒshowã¯åç
§ããŠããã¢ã¯ã·ã§ã³ã§ãã3çªç®ã®åŒæ°ã¯ã¢ãã«ãŸãã¯ãªããžã§ã¯ãèå¥åã®ããããã§ãã ãããããããããæ©äŒããããŸãã
post_path(conn, :show, 1)
åæã«ããã¹ãããããªãœãŒã¹ãããå Žåããã«ããŒã¯routeãã¡ã€ã«ã®å€æŽãšãšãã«å€æŽãããŸãã ç§ãã¡ã®å ŽåïŒ
user_post_path(conn, :show, user, post)
3çªç®ã®åŒæ°ã¯å€éšãªãœãŒã¹ãè¡šãããã¹ããããååŒæ°ã次ã«æ¥ãããšã«æ³šæããŠãã ããã
ãšã©ãŒãçºçããçç±ãããã£ãã®ã§ããšã©ãŒãä¿®æ£ã§ããŸãã åã³ã³ãããŒã©ãŒã¢ã¯ã·ã§ã³ã§ãèŠæ±ããããŠãŒã¶ãŒã«ã¢ã¯ã»ã¹ããå¿
èŠããããŸãã ãããååŸããæè¯ã®æ¹æ³ã¯ããã©ã°ã€ã³ã䜿çšããããšã§ãã ãããè¡ãã«ã¯ã
web / controllers / post_controller.exãã¡ã€ã«ãéããäžçªäžã®æ°ãããã©ã°ã€ã³ãžã®åŒã³åºããè¿œå ããŸãã
plug :assign_user
ãããŠããããå°ãäžã«æžããŸãã
defp assign_user(conn, _opts) do case conn.params do %{"user_id" => user_id} -> user = Repo.get(Pxblog.User, user_id) assign(conn, :user, user) _ -> conn end end
ãããŠãã©ãã§ã
post_pathã
user_post_pathã«çœ®ãæã
ãŸã ïŒ
def create(conn, %{"post" => post_params}) do changeset = Post.changeset(%Post{}, post_params) case Repo.insert(changeset) do {:ok, _post} -> conn |> put_flash(:info, "Post created successfully.") |> redirect(to: user_post_path(conn, :index, conn.assigns[:user])) {:error, changeset} -> render(conn, "new.html", changeset: changeset) end end def update(conn, %{"id" => id, "post" => post_params}) do post = Repo.get!(Post, id) changeset = Post.changeset(post, post_params) case Repo.update(changeset) do {:ok, post} -> conn |> put_flash(:info, "Post updated successfully.") |> redirect(to: user_post_path(conn, :show, conn.assigns[:user], post)) {:error, changeset} -> render(conn, "edit.html", post: post, changeset: changeset) end end def delete(conn, %{"id" => id}) do post = Repo.get!(Post, id)
ãã³ãã¬ãŒããæŽçãã
ã³ã³ãããŒã©ãŒããšã©ãŒã¡ãã»ãŒãžã®åºåãåæ¢ããã®ã§ãä»åºŠã¯ãã³ãã¬ãŒããäœæããŸãã ä»»æã®ã³ã³ãããŒã©ãŒã¢ã¯ã·ã§ã³ããã¢ã¯ã»ã¹ã§ãããã©ã°ã€ã³ãå®è£
ããããšã§ãçãéãæ©ã¿ãŸããã æ¥ç¶ãªããžã§ã¯ãã§
assigné¢æ°ã䜿çšããŠããã³ãã¬ãŒãã§äœ¿çšã§ããå€æ°ãå®çŸ©ããŸãã 次ã«ããã³ãã¬ãŒããå°ãå€æŽããŠãpost_pathãã«ããŒãuser_post_pathã«çœ®ãæããã¢ã¯ã·ã§ã³åã®åŸã®æ¬¡ã®åŒæ°ããŠãŒã¶ãŒèå¥åã§ããããšã確èªããŸãã
web / templates / post / index.html.eexãã¡ã€ã«ã§ã次ã®ããã«æžããŸãïŒ
<h2>Listing posts</h2> <table class="table"> <thead> <tr> <th>Title</th> <th>Body</th> <th></th> </tr> </thead> <tbody> <%= for post <- @posts do %> <tr> <td><%= post.title %></td> <td><%= post.body %></td> <td class="text-right"> <%= link "Show", to: user_post_path(@conn, :show, @user, post), class: "btn btn-default btn-xs" %> <%= link "Edit", to: user_post_path(@conn, :edit, @user, post), class: "btn btn-default btn-xs" %> <%= link "Delete", to: user_post_path(@conn, :delete, @user, post), method: :delete, data: [confirm: "Are you sure?"], class: "btn btn-danger btn-xs" %> </td> </tr> <% end %> </tbody> </table> <%= link "New post", to: user_post_path(@conn, :new, @user) %>
web / templates / post / show.html.eexãã¡ã€ã«ã§ïŒ
<h2>Show post</h2> <ul> <li> <strong>Title:</strong> <%= @post.title %> </li> <li> <strong>Body:</strong> <%= @post.body %> </li> </ul> <%= link "Edit", to: user_post_path(@conn, :edit, @user, @post) %> <%= link "Back", to: user_post_path(@conn, :index, @user) %>
web / templates / post / new.html.eexãã¡ã€ã«ã§ ïŒ
<h2>New post</h2> <%= render "form.html", changeset: @changeset, action: user_post_path(@conn, :create, @user) %> <%= link "Back", to: user_post_path(@conn, :index, @user) %>
web / templates / post / edit.html.eexãã¡ã€ã«ã§ ïŒ
<h2>Edit post</h2> <%= render "form.html", changeset: @changeset, action: user_post_path(@conn, :update, @user, @post) %> <%= link "Back", to: user_post_path(@conn, :index, @user) %>
ããŠããã«ã¹ãã§ãã¯ãšããŠã
mix phoenix.routes
ãå®è¡ãããšããã¹ã®åºåãšã³ã³ãã€ã«ã®æåã衚瀺ãããã¯ãã§ãïŒ
Compiling 14 files (.ex) page_path GET / Pxblog.PageController :index user_path GET /users Pxblog.UserController :index user_path GET /users/:id/edit Pxblog.UserController :edit user_path GET /users/new Pxblog.UserController :new user_path GET /users/:id Pxblog.UserController :show user_path POST /users Pxblog.UserController :create user_path PATCH /users/:id Pxblog.UserController :update PUT /users/:id Pxblog.UserController :update user_path DELETE /users/:id Pxblog.UserController :delete user_post_path GET /users/:user_id/posts Pxblog.PostController :index user_post_path GET /users/:user_id/posts/:id/edit Pxblog.PostController :edit user_post_path GET /users/:user_id/posts/new Pxblog.PostController :new user_post_path GET /users/:user_id/posts/:id Pxblog.PostController :show user_post_path POST /users/:user_id/posts Pxblog.PostController :create user_post_path PATCH /users/:user_id/posts/:id Pxblog.PostController :update PUT /users/:user_id/posts/:id Pxblog.PostController :update user_post_path DELETE /users/:user_id/posts/:id Pxblog.PostController :delete session_path GET /sessions/new Pxblog.SessionController :new session_path POST /sessions Pxblog.SessionController :create session_path DELETE /sessions/:id Pxblog.SessionController :delete
æ®ãã®éšåãã³ã³ãããŒã©ãŒã«æ¥ç¶ããŸã
ããã§ãå¿
èŠãªã®ã¯ãæ°ããé¢é£ä»ãã䜿çšããããã«ã³ã³ãããŒã©ãŒã§äœæ¥ãå®äºããããšã ãã§ãã
iex -S mix
ã³ãã³ãã䜿çšããŠå¯Ÿè©±åã³ã³ãœãŒã«ãèµ·åãããŠãŒã¶ãŒã®æçš¿ãéžæããæ¹æ³ã«ã€ããŠå°ãåŠç¿ããŸãã ãã ãããã®åã«ãiexã³ã³ãœãŒã«ããããžã§ã¯ãå
ã«èªã¿èŸŒãŸãããã³ã«èªã¿èŸŒãŸããæšæºã®ã€ã³ããŒã/ãšã€ãªã¢ã¹ã®ãªã¹ããèšå®ããå¿
èŠããããŸãã ãããžã§ã¯ãã«ãŒãã«æ°ãã
.iex.exsãã¡ã€ã«ãäœæãïŒãã¡ã€ã«åã®å
é ã«ãããããã«æ³šæããŠãã ããïŒã次ã®å
容ãå
¥åããŸãã
import Ecto.Query alias Pxblog.User alias Pxblog.Post alias Pxblog.Repo import Ecto
ä»ãiexãèµ·åãããšããæ¯åãã®ãããªããšãããå¿
èŠã¯ãããŸããïŒ
iex(1)> import Ecto.Query nil iex(2)> alias Pxblog.User nil iex(3)> alias Pxblog.Post nil iex(4)> alias Pxblog.Repo nil iex(5)> import Ecto nil
次ã«ããªããžããªã«å°ãªããšã1人ã®ãŠãŒã¶ãŒãå¿
èŠã§ãã ããã§ãªãå Žåã¯ãè¿œå ããŸãã ãã®åŸã次ãå®è¡ã§ããŸãã
iex(8)> user = Repo.get(User, 1) [debug] SELECT u0."id", u0."username", u0."email", u0."password_digest", u0."inserted_at", u0."updated_at" FROM "users" AS u0 WHERE (u0."id" = $1) [1] OK query=8.2ms %Pxblog.User{__meta__:
ãããŸã§ã®ãšããããã®ãŠãŒã¶ãŒã®æçš¿ã1ã€ãäœæããŠããªããããããã§ç©ºã®ãªã¹ããååŸããã®ãè«ççã§ãã
Ectoã®assocé¢æ°ã䜿çšããŠãæçš¿ããŠãŒã¶ãŒã«ãªã³ã¯ãããªã¯ãšã¹ããååŸããŸããã 次ã®ããšãã§ããŸãã
iex(14)> Repo.all from p in Post, ...(14)> join: u in assoc(p, :user), ...(14)> select: p [debug] SELECT p0."id", p0."title", p0."body", p0."user_id", p0."inserted_at", p0."updated_at" FROM "posts" AS p0 INNER JOIN "users" AS u1 ON u1."id" = p0."user_id" [] OK query=0.9ms
ããã§ã¯ããŠãŒã¶ãŒIDã§éžæããããã®çŽæ¥æ¡ä»¶ã§ã¯ãªããå
éšçµåã䜿çšããŠèŠæ±ãäœæãããŸãã äž¡æ¹ã®å Žåã«çæãããã¯ãšãªãã©ã®ããã«èŠãããã«ç¹ã«æ³šæããŠãã ããã ã¯ãšãªãçæããã³ãŒããæäœãããšãã¯ãã€ã§ãããèå°è£ãã§äœæãããSQLãç解ããããšã¯éåžžã«åœ¹ç«ã¡ãŸãã
以äžã«ç€ºãããã«ããŠãŒã¶ãŒãããªããŒãããããã«æçš¿ãååŸãããšãã«
ããªããŒãæ©èœã䜿çšããããšãã§ããŸãã
iex(18)> Repo.all(from u in User, preload: [:posts]) [debug] SELECT u0."id", u0."username", u0."email", u0."password_digest", u0."inserted_at", u0."updated_at" FROM "users" AS u0 [] OK query=0.9ms [debug] SELECT p0."id", p0."title", p0."body", p0."user_id", p0."inserted_at", p0."updated_at" FROM "posts" AS p0 WHERE (p0."user_id" IN ($1)) ORDER BY p0."user_id" [1] OK query=0.8ms iex(20)> Repo.all(from p in Post, preload: [:user]) [debug] SELECT p0."id", p0."title", p0."body", p0."user_id", p0."inserted_at", p0."updated_at" FROM "posts" AS p0 [] OK query=0.8ms []
ãªã¯ãšã¹ãã«æãå ããããšãã§ããããã«ãæçš¿ãè¿œå ããå¿
èŠããããŸãã ãã®ããããã®ããã«
build_assocãšåŒã°ãã
Ectoé¢æ°ã䜿çšããŸãã ãã®é¢æ°ã¯ãã¢ãœã·ãšãŒã·ã§ã³ãè¿œå ãããã¢ãã«ã®æåã®åŒæ°ãšãã¢ãã ã®åœ¢ã®ã¢ãœã·ãšãŒã·ã§ã³èªäœãåããŸãã
iex(1)> user = Repo.get(User, 1) iex(2)> post = build_assoc(user, :posts, %{title: "Test Title", body: "Test Body"}) iex(3)> Repo.insert(post) iex(4)> posts = Repo.all(from p in Post, preload: [:user])
ãããŠä»ãæåŸã®ãªã¯ãšã¹ããå®äºãããã次ã®åºåãååŸããå¿
èŠããããŸãã
iex(4)> posts = Repo.all(from p in Post, preload: [:user]) [debug] SELECT p0."id", p0."title", p0."body", p0."user_id", p0."inserted_at", p0."updated_at" FROM "posts" AS p0 [] OK query=0.7ms [debug] SELECT u0."id", u0."username", u0."email", u0."password_digest", u0."inserted_at", u0."updated_at" FROM "users" AS u0 WHERE (u0."id" IN ($1)) [1] OK query=0.7ms [%Pxblog.Post{__meta__:
ãããŠãæåã®çµæããã°ãã確èªããŸãã
iex(5)> post = List.first posts %Pxblog.Post{__meta__:
ãã£ãããïŒ ç§ãã¡ã®å®éšã¯ç§ãã¡ãæåŸ
ãããã®ãæ£ç¢ºã«ç€ºããã®ã§ãã³ã³ãããŒã©ãŒïŒãã¡ã€ã«
web / controllers / post_controller.ex ïŒã«
æ»ã£ãŠã³ãŒãã®
ç·šéãéå§
ããŸãã
ã€ã³ããã¯ã¹ã¢ã¯ã·ã§ã³ã§ã¯ããŠãŒã¶ãŒã«é¢é£ãããã¹ãŠã®æçš¿ãååŸããŸãã ããããå§ããŸãããïŒ
def index(conn, _params) do posts = Repo.all(assoc(conn.assigns[:user], :posts)) render(conn, "index.html", posts: posts) end
ããã§ãæåã®ãŠãŒã¶ãŒã®æçš¿ã®ãªã¹ããèŠãããšãã§ããŸãïŒ ããããååšããªããŠãŒã¶ãŒã®æçš¿ã®ãªã¹ããååŸããããšãããšãUXãæªããšãããšã©ãŒã¡ãã»ãŒãžã衚瀺ãããã®ã§ã
assign_userãã©ã°ã€ã³ãæŽçããŸãããã
defp assign_user(conn, _opts) do case conn.params do %{"user_id" => user_id} -> case Repo.get(Pxblog.User, user_id) do nil -> invalid_user(conn) user -> assign(conn, :user, user) end _ -> invalid_user(conn) end end defp invalid_user(conn) do conn |> put_flash(:error, "Invalid user!") |> redirect(to: page_path(conn, :index)) |> halt end
ããã§ãååšããªããŠãŒã¶ãŒã®æçš¿ã®ãªã¹ããéããšãçŽ æµãªãã©ãã·ã¥ã¡ãã»ãŒãžã衚瀺ããã芪åã«
page_pathã«ãªãã€ã¬ã¯ããã
ãŸã ã 次ã«ã
æ°ããã¢ã¯ã·ã§ã³ãå€æŽããå¿
èŠããããŸãã
def new(conn, _params) do changeset = conn.assigns[:user] |> build_assoc(:posts) |> Post.changeset() render(conn, "new.html", changeset: changeset) end
ãŠãŒã¶ãŒã¢ãã«ãååŸããããã
build_assocé¢æ°ã«æž¡ããæçš¿ãäœæããå¿
èŠããããšèšã£ãŠãããçµæã®ç©ºã®ã¢ãã«ã
Post.changeseté¢æ°ã«
æž¡ããŠç©ºã®ãªããžã§ã³ãååŸããŸãã createã¡ãœããã«ã€ããŠãåãæ¹æ³ã§è¡ããŸãïŒ
post_paramsã®è¿œå ã
é€ã ïŒïŒ
def create(conn, %{"post" => post_params}) do changeset = conn.assigns[:user] |> build_assoc(:posts) |> Post.changeset(post_params) case Repo.insert(changeset) do {:ok, _post} -> conn |> put_flash(:info, "Post created successfully.") |> redirect(to: user_post_path(conn, :index, conn.assigns[:user])) {:error, changeset} -> render(conn, "new.html", changeset: changeset) end end
ãããŠã
show ã
edit ã
update ãããã³
deleteã®ã¢ã¯ã·ã§ã³ãå€æŽã
ãŸã ïŒ
def show(conn, %{"id" => id}) do post = Repo.get!(assoc(conn.assigns[:user], :posts), id) render(conn, "show.html", post: post) end def edit(conn, %{"id" => id}) do post = Repo.get!(assoc(conn.assigns[:user], :posts), id) changeset = Post.changeset(post) render(conn, "edit.html", post: post, changeset: changeset) end def update(conn, %{"id" => id, "post" => post_params}) do post = Repo.get!(assoc(conn.assigns[:user], :posts), id) changeset = Post.changeset(post, post_params) case Repo.update(changeset) do {:ok, post} -> conn |> put_flash(:info, "Post updated successfully.") |> redirect(to: user_post_path(conn, :show, conn.assigns[:user], post)) {:error, changeset} -> render(conn, "edit.html", post: post, changeset: changeset) end end def delete(conn, %{"id" => id}) do post = Repo.get!(assoc(conn.assigns[:user], :posts), id)
ãã¹ãŠã®ãã¹ããå®è¡ããåŸããã¹ãŠãæ©èœããããšã確èªããå¿
èŠããããŸãã ãããé€ããŠ...ãã¹ãŠã®ãŠãŒã¶ãŒã¯ãèªåãæããŠãŒã¶ãŒã®äžã§æ°ããæçš¿ãåé€/ç·šé/äœæããããšãã§ããŸãïŒ
ãŠãŒã¶ãŒã«ããæçš¿ã®äœæãå¶éããŸã
ãã®ãããªã»ãã¥ãªãã£ããŒã«ã®ããããã°ãšã³ãžã³ã¯ãªãªãŒã¹ã§ããŸããã åä¿¡ãããŠãŒã¶ãŒãçŸåšã®ãŠãŒã¶ãŒã§ãããããšãä¿èšŒããå¥ã®ãã©ã°ã€ã³ãè¿œå ããŠããããä¿®æ£ããŸãããã
web / controllers / post_controller.exãã¡ã€ã«ã®æåŸã«æ°ããé¢æ°ãè¿œå ã
ãŸã ïŒ
defp authorize_user(conn, _opts) do user = get_session(conn, :current_user) if user && Integer.to_string(user.id) == conn.params["user_id"] do conn else conn |> put_flash(:error, "You are not authorized to modify that post!") |> redirect(to: page_path(conn, :index)) |> halt() end end
ãããŠäžçªäžã«ããã©ã°ã€ã³åŒã³åºããè¿œå ããŸãïŒ
plug :authorize_user when action in [:new, :create, :update, :edit, :delete]
ããã§ãã¹ãŠãããŸãããã¯ãã§ãïŒ æçš¿ããã«ã¯ããŠãŒã¶ãŒãç»é²ããå¿
èŠããããŸãããã®åŸããŠãŒã¶ãŒã®ã¿ãšäœæ¥ããŸãã æ®ã£ãŠããã®ã¯ããã¹ãã¹ã€ãŒããæŽæ°ããŠãããã®å€æŽãåŠçããã ãã§ãã éå§ããã«ã¯ã
æ··åãã¹ããå®è¡ããŠçŸåšã®ç¶æ³ãè©äŸ¡ããã ãã§ãã ã»ãšãã©ã®å Žåã次ã®ãšã©ãŒã衚瀺ãããŸãã
** (CompileError) test/controllers/post_controller_test.exs:14: function post_path/2 undefined (stdlib) lists.erl:1337: :lists.foreach/2 (stdlib) erl_eval.erl:669: :erl_eval.do_apply/6 (elixir) lib/code.ex:363: Code.require_file/2 (elixir) lib/kernel/parallel_require.ex:50: anonymous fn/4 in Kernel.ParallelRequire.spawn_requires/5
æ®å¿µãªãããå
post_pathåŒã³åºããåã³
user_post_pathã«å€æŽããå¿
èŠããããŸãã ãããè¡ãã«ã¯ããã¹ããæ ¹æ¬çã«å€æŽããå¿
èŠããããŸãã
test / controllers / post_controller_text.exsãã¡ã€ã«ã«èšå®ãããã¯ãè¿œå ããããšãã
å§ããŸã ã
alias Pxblog.User setup do {:ok, user} = create_user conn = build_conn() |> login_user(user) {:ok, conn: conn, user: user} end defp create_user do User.changeset(%User{}, %{email: "test@test.com", username: "test", password: "test", password_confirmation: "test"}) |> Repo.insert end defp login_user(conn, user) do post conn, session_path(conn, :create), user: %{username: user.username, password: user.password} end
ããã§ã¯å€ãã®ããšãè¡ãããŠããŸãã æåã«ããããšã¯ãäœæããå¿
èŠããã
create_useré¢æ°ãžã®åŒã³åºããè¿œå ããããšã§ããã ãã¹ãã«ã¯ãã«ããŒãå¿
èŠãªã®ã§ãè¿œå ããŸãããã
create_useré¢æ°ã¯åçŽã«ãã¹ããŠãŒã¶ãŒã
Repoã«è¿œå ããããããã®é¢æ°ãåŒã³åºããšãã«ãã¿ãŒã³ãããã³ã°
{ïŒokãuser}ã䜿çšããŸãã
次ã«ã
åè¿°ã®ããã«
conn = build_connïŒïŒãåŒã³åºããŸãã 次ã«ã
connã®çµæã
login_useré¢æ°ã«
æž¡ããŸãã ãã¹ãŠã®åºæ¬çãªæçš¿ã¢ã¯ã·ã§ã³ã«ã¯ãŠãŒã¶ãŒãå¿
èŠãªãããããã«ããæçš¿ããã°ã€ã³æ©èœã«æ¥ç¶ãããŸãã
connãè¿ããåã
ã®ãã¹ãã«æã¡èŸŒãå¿
èŠãããããšãç解ããããšã¯éåžžã«éèŠã§ãã ããããªããšããŠãŒã¶ãŒã¯ãã°ã€ã³ãããŸãŸã«ãªããŸããã
æåŸã«ããã®é¢æ°ã®æ»ãå€ãæšæºå€ã®æ»ãå€
okããã³
ïŒconnã«å€æŽããŸããããèŸæžã«å¥ã®ãšã³ããª
userãå«ã
ãŸã ã å€æŽããæåã®ãã¹ããèŠãŠã¿ãŸãããã
test "lists all entries on index", %{conn: conn, user: user} do conn = get conn, user_post_path(conn, :index, user) assert html_response(conn, 200) =~ "Listing posts" end
testã¡ãœããã®2çªç®ã®åŒæ°ãå€æŽããŠããã¿ãŒã³ãããã³ã°ã䜿çšããŠãkey
ïŒconnã«å ããŠkey
ïŒuserãå«ãèŸæžãååŸããããšã«æ³šæããŠãã ããã ããã«ããã
ã»ããã¢ãããããã¯ã§äœæ¥ãã
ãŠãŒã¶ãŒããŒã䜿çšããããšãä¿èšŒãã
ãŸã ã ããã«ã
post_pathãã«ããŒã®åŒã³åºãã
user_post_pathã«å€æŽãã3çªç®ã®åŒæ°ãæã€ãŠãŒã¶ãŒãè¿œå ããŸããã ãã®ãã¹ãã®ã¿ãçŽæ¥å®è¡ããŸãã ãããè¡ãã«ã¯ãã¿ã°ãæå®ãããã次ã®ããã«ã³ãã³ããå®è¡ããŠç®çã®è¡ã®çªå·ãæå®ããŸãã
$ mix test test/controller/post_controller_test.exs:[line number]
ãã¹ããç·è²ã«å€ããã¯ãã§ãïŒ ãããïŒ ãã®éšåãå€æŽããŸãããïŒ
test "renders form for new resources", %{conn: conn, user: user} do conn = get conn, user_post_path(conn, :new, user) assert html_response(conn, 200) =~ "New post" end
ããã§ã¯ã
ã»ããã¢ãããã³ãã©ãšãã¹ãå€æŽãã以å€ã«æ°ãããã®ã¯ãããŸããã
test "creates resource and redirects when data is valid", %{conn: conn, user: user} do conn = post conn, user_post_path(conn, :create, user), post: @valid_attrs assert redirected_to(conn) == user_post_path(conn, :index, user) assert Repo.get_by(assoc(user, :posts), @valid_attrs) end
ãŠãŒã¶ãŒã«é¢é£ä»ãããããã¹ãŠã®æçš¿ãåä¿¡ããå¿
èŠãããããšãå¿ããªãã§ãã ããã
ãããã£ãŠããã¹ãŠã®åŒã³åºãã
post_pathã«å€æŽããŸãã
test "does not create resource and renders errors when data is invalid", %{conn: conn, user: user} do conn = post conn, user_post_path(conn, :create, user), post: @invalid_attrs assert html_response(conn, 200) =~ "New post" end
å¥ã®ãããã«å€æŽããããã¹ãã èŠãã¹ããã®ã¯äœããªãã®ã§ã次ã«èå³æ·±ããã®ã«ç§»ããŸãããã ãŠãŒã¶ãŒã¢ãœã·ãšãŒã·ã§ã³ã«å±ããæçš¿ãäœæ/åä¿¡ããããšãæãåºããŠãã ãããããã§ã
ãshows selected resourceããã¹ãã®å€æŽã«é²ã¿ãŸãã
test "shows chosen resource", %{conn: conn, user: user} do post = build_post(user) conn = get conn, user_post_path(conn, :show, user, post) assert html_response(conn, 200) =~ "Show post" end
以åã¯ãåçŽãª
Repo.insert! %Post{}
ã䜿çšããŠæçš¿ãè¿œå ããŸãã
Repo.insert! %Post{}
Repo.insert! %Post{}
ã ããã¯ããæ©èœããªãã®ã§ãæ£ããé¢é£ä»ãã§äœæããå¿
èŠããããŸãã ãã®è¡ã¯æ®ãã®ãã¹ãã§éåžžã«é »ç¹ã«äœ¿çšãããããããã®äœ¿çšã容æã«ãããã«ããŒãäœæããŸãã
defp build_post(user) do changeset = user |> build_assoc(:posts) |> Post.changeset(@valid_attrs) Repo.insert!(changeset) end
ãã®ã¡ãœããã¯ããŠãŒã¶ãŒã«é¢é£ä»ããããæå¹ãªæçš¿ã¢ãã«ãäœæããããŒã¿ããŒã¹ã«æ¿å
¥ããŸããRepo.insertïŒã«æ³šæããŠãã ãããæ»ããªã{ïŒ[OK]ã¶ã»ã¢ãã«ã}ãããã³ã¢ãã«èªäœãè¿ããŸãïŒå€æŽãããã¹ãã«æ»ããŸããããæ®ãã®ãã¹ããã¬ã€ã¢ãŠããããã¹ãŠã®ãã¹ãã«åæ ŒãããŸã§å¯Ÿå¿ããå€æŽã1ã€ãã€ç¹°ãè¿ããŸãã test "renders page not found when id is nonexistent", %{conn: conn, user: user} do assert_raise Ecto.NoResultsError, fn -> get conn, user_post_path(conn, :show, user, -1) end end test "renders form for editing chosen resource", %{conn: conn, user: user} do post = build_post(user) conn = get conn, user_post_path(conn, :edit, user, post) assert html_response(conn, 200) =~ "Edit post" end test "updates chosen resource and redirects when data is valid", %{conn: conn, user: user} do post = build_post(user) conn = put conn, user_post_path(conn, :update, user, post), post: @valid_attrs assert redirected_to(conn) == user_post_path(conn, :show, user, post) assert Repo.get_by(Post, @valid_attrs) end test "does not update chosen resource and renders errors when data is invalid", %{conn: conn, user: user} do post = build_post(user) conn = put conn, user_post_path(conn, :update, user, post), post: %{"body" => nil} assert html_response(conn, 200) =~ "Edit post" end test "deletes chosen resource", %{conn: conn, user: user} do post = build_post(user) conn = delete conn, user_post_path(conn, :delete, user, post) assert redirected_to(conn) == user_post_path(conn, :index, user) refute Repo.get(Post, post.id) end
ãããããã¹ãŠä¿®æ£ããããmix testã³ãã³ããå®è¡ããŠãã°ãªãŒã³ãã¹ããååŸã§ããŸãïŒæåŸã«ããŠãŒã¶ãŒã®æ€çŽ¢ãšæ¿èªãåŠçããããã®ãã©ã°ã€ã³ãªã©ã®æ°ããã³ãŒããäœæããæåããã±ãŒã¹ãéåžžã«ãããã¹ãããŸããããè² ã®ã±ãŒã¹ã®ãã¹ããè¿œå ããå¿
èŠããããŸããååšããªããŠãŒã¶ãŒããã®æçš¿ã«ã¢ã¯ã»ã¹ããããšãããšäœãèµ·ãããããã¹ãããããšããå§ããŸãã test "redirects when the specified user does not exist", %{conn: conn} do conn = get conn, user_post_path(conn, :index, -1) assert get_flash(conn, :error) == "Invalid user!" assert redirected_to(conn) == page_path(conn, :index) assert conn.halted end
ããã§ã¯äœ¿çšããªããããã»ããã¢ãããããã¯ã®ãµã³ãã«ãšæ¯èŒããŠïŒuserã¯å«ããŸããã§ãããæ¥ç¶ãæåŸã«éããããšã確èªããŸãããããŠæåŸã«ã誰ãã®æçš¿ãç·šéããããšãããã¹ããæžãå¿
èŠããããŸãã test "redirects when trying to edit a post for a different user", %{conn: conn, user: user} do other_user = User.changeset(%User{}, %{email: "test2@test.com", username: "test2", password: "test", password_confirmation: "test"}) |> Repo.insert! post = build_post(user) conn = get conn, user_post_path(conn, :edit, other_user, post) assert get_flash(conn, :error) == "You are not authorized to modify that post!" assert redirected_to(conn) == page_path(conn, :index) assert conn.halted end
æªããŠãŒã¶ãŒã«ãªãå¥ã®ãŠãŒã¶ãŒãäœæãã圌ãRepoã«è¿œå ããŸãã次ã«ãæåã®ãŠãŒã¶ãŒã®æçš¿ã®ç·šéã¢ã¯ã·ã§ã³ã«ã¢ã¯ã»ã¹ããããšããŸããããã«ãããauthorize_userãã©ã°ã€ã³ã®ãã€ãã¹ã®ã±ãŒã¹ãæ©èœããŸãïŒãã¡ã€ã«ãä¿åããã³ãã³ãmix test
ãå®è¡ããŠçµæãåŸ
ã¡ãŸãïŒ ....................................... Finished in 0.4 seconds 39 tests, 0 failures Randomized with seed 102543
è¡ããïŒç§ãã¡ã¯ãããããã£ãïŒããããä»ã§ã¯æ©èœçãªïŒãããŠããå®å
šãªïŒããã°ãããããŠãŒã¶ãŒåãã®æçš¿ãäœæãããŠããŸãããããŠãæã
ã¯ãŸã è¯ããã¹ãã«ãã¬ããžãæã£ãŠããŸãïŒäŒæ©ããæéã§ãã管çè
ã®åœ¹å²ãã³ã¡ã³ããMarkdownãµããŒããè¿œå ããããšã§ããã®äžé£ã®ãã¬ãŒãã³ã°è³æãç¶ç¶ããæçµçã«ã©ã€ãã³ã¡ã³ãã·ã¹ãã ã§ãã£ã³ãã«ã«äŸµå
¥ããŸãïŒç¿»èš³è
ããã®éèŠãªçµè«
ç§ã¯ãã®èšäºãšã·ãªãŒãºå
šäœã®ç¿»èš³ã®äž¡æ¹ã翻蚳ããçŽ æŽãããä»äºãããŸãããç§ãä»ãç¶ããŠããããšããããã£ãŠãèšäºèªäœãŸãã¯RuNetã§Elixirãæ®åãããåªåãæ°ã«å
¥ã£ãå Žåã¯ããã©ã¹ãã³ã¡ã³ããåæçš¿ã§èšäºããµããŒãããŠãã ãããããã¯ç§å人ã«ãšã£ãŠãããšãªã¯ãµãŒã³ãã¥ããã£å
šäœã«ãšã£ãŠãéåžžã«éèŠã§ããã·ãªãŒãºã®ä»ã®èšäº
- ãšã³ããªãŒ
- ãã°ã€ã³
- 圹å²ãè¿œå
- ã³ã³ãããŒã©ãŒã§åœ¹å²ãåŠçããŸã
- ExMachinaãæ¥ç¶ããŸã
- ããŒã¯ããŠã³ã®ãµããŒã
- ã³ã¡ã³ããè¿œå
- ã³ã¡ã³ãã§çµäº
- ãã£ã³ãã«
- ãã£ãã«ãã¹ã
- ãããã«
ãã¹ãŠã®äžæ£ç¢ºãããšã©ãŒãäžååãªç¿»èš³ã«ã€ããŠã¯ãå人çãªã¡ãã»ãŒãžã§æžããŠãã ãããç§ã¯ããã«ãããä¿®æ£ããŸããäºåã«æè¬ããŸãã