FROM elixir:1.14-alpine
RUN apk update && apk add \
inotify-tools git build-base npm bash
RUN mix do local.hex --force, local.rebar --force, archive.install --force hex phx_new
WORKDIR /app
docker-compose.yml
build
$ docker-compose build
ElixirとPhoenixのバージョンを確認。
Phoenixプロジェクト作成
$ docker-compose run web mix phx.new . --app todo_phoenix --database mysql --no-assets --no-html --no-gettext
# The directory /app already exists. Are you sure you want to continue? [Yn] Y
# Fetch and install dependencies? [Yn] Y
defmodule TodoPhoenix.Todo.Task do
use Ecto.Schema
import Ecto.Changeset
schema "todo_tasks" do
field :content, :string
field :state, :integer
timestamps()
end
@doc false
def changeset(task, attrs) do
task
|> cast(attrs, [:content, :state])
|> validate_required([:content, :state])
end
end
defmodule TodoPhoenixWeb.TaskView do
use TodoPhoenixWeb, :view
alias TodoPhoenixWeb.TaskView
def render("index.json", %{tasks: tasks}) do
%{data: render_many(tasks, TaskView, "task.json")}
end
def render("task.json", %{task: task}) do
%{
id: task.id,
content: task.content,
state: task.state
}
end
end
lib/todo_phoenix_web/router.ex
scope "/api", TodoPhoenixWeb do
pipe_through :api
resources "/tasks", TaskController, only: [:create, :index] # 追加
end
defmodule TodoPhoenix.Todo.Task do
use Ecto.Schema
import Ecto.Changeset
@type t :: %__MODULE__{} # 追加。これを追加することでmodule名.t()で他から参照できるようになります。
schema "todo_tasks" do
defmodule TodoPhoenix.Todo do
import Ecto.Query, warn: false
alias TodoPhoenix.Repo
alias TodoPhoenix.Todo.Task
@doc """
タスク一覧を取得する。
"""
def list_tasks do
Repo.all(Task)
end
@doc """
タスクを作成する。
"""
def create_task(attrs \\ %{}) do
%Task{}
|> Task.changeset(attrs)
|> Repo.insert()
end
end
defmodule TodoPhoenixWeb.TaskController do
use TodoPhoenixWeb, :controller
alias TodoPhoenix.Todo
alias TodoPhoenix.Todo.Task
def index(conn, _params) do
tasks = Todo.list_tasks()
render(conn, "index.json", tasks: tasks)
end
def create(conn, %{"task" => task_params}) do
case Todo.create_task(task_params) do
{:ok, %Task{}} ->
send_resp(conn, :created, "")
{:error, _} ->
send_resp(conn, :unprocessable_entity, "")
end
end
def create(conn, _), do: send_resp(conn, :bad_request, "")
end
# GET tasks
curl http://localhost:4000/api/tasks | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 11 100 11 0 0 53 0 --:--:-- --:--:-- --:--:-- 56
{
"data": []
}
# データは空
# POST tasks
$ curl -v http://localhost:4000/api/tasks -H "Content-Type: application/json" -d '{"task":{"content": "foo"}}'
* Trying 127.0.0.1:4000...
* Connected to localhost (127.0.0.1) port 4000 (#0)
> POST /api/tasks HTTP/1.1
> Host: localhost:4000
> User-Agent: curl/7.79.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 27
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 201 Created
# GET tasks
curl http://localhost:4000/api/tasks | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 50 100 50 0 0 277 0 --:--:-- --:--:-- --:--:-- 294
{
"data": [
{
"content": "foo",
"id": 4,
"state": "new"
}
]
}
# データが作成されている
# POST tasks
curl -v http://localhost:4000/api/tasks -H "Content-Type: application/json" -d '{"task":{"content": "bar"}}'
* Trying 127.0.0.1:4000...
* Connected to localhost (127.0.0.1) port 4000 (#0)
> POST /api/tasks HTTP/1.1
> Host: localhost:4000
> User-Agent: curl/7.79.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 27
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 201 Created
# GET tasks
curl http://localhost:4000/api/tasks | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 90 100 90 0 0 511 0 --:--:-- --:--:-- --:--:-- 545
{
"data": [
{
"content": "foo",
"id": 4,
"state": "new"
},
{
"content": "bar",
"id": 5,
"state": "new"
}
]
}
# 追加で1件データが作成されている
@@ -7,8 +7,8 @@
# Run `mix help test` for more information.
config :todo_phoenix, TodoPhoenix.Repo,
username: "root",
- password: "",
- hostname: "localhost",
+ password: "password", # dockerの設定情報に合わせてDBの接続情報を編集
+ hostname: "db", # dockerの設定情報に合わせてDBの接続情報を編集
database: "todo_phoenix_test#{System.get_env("MIX_TEST_PARTITION")}",
pool: Ecto.Adapters.SQL.Sandbox,
pool_size: 10
defmodule TodoPhoenix.TodoTest do
use TodoPhoenix.DataCase
import TodoPhoenix.TodoFixtures
alias TodoPhoenix.Todo
alias TodoPhoenix.Todo.Task
describe "task.list_tasks" do
test "taskが配列で取得できること" do
task = task_fixture()
assert Todo.list_tasks() == [task]
end
end
describe "task.create_task" do
@valid_attrs %{content: "bar", state: :doing}
@invalid_attrs %{content: nil}
test "taskが登録できること" do
assert {:ok, %Task{} = task} = Todo.create_task(@valid_attrs)
assert task.content == "bar"
assert task.state == :doing
end
test "パラメータが不正な場合、Changesetエラーが返却されること" do
assert {:error, %Ecto.Changeset{}} = Todo.create_task(@invalid_attrs)
end
end
end
defmodule TodoPhoenixWeb.TaskControllerTest do
use TodoPhoenixWeb.ConnCase
@create_attrs %{
content: "bar",
state: :new
}
@invalid_attrs %{content: nil}
@blank_params %{}
setup %{conn: conn} do
{:ok, conn: put_req_header(conn, "accept", "application/json")}
end
describe "index" do
test "ステータスコード200を返却すること", %{conn: conn} do
conn = get(conn, Routes.task_path(conn, :index))
assert json_response(conn, :ok)["data"] == []
end
end
describe "create task" do
test "登録に成功した場合、ステータスコード201を返却すること", %{conn: conn} do
conn = post(conn, Routes.task_path(conn, :create), task: @create_attrs)
assert response(conn, :created)
end
test "パラメータが不正な場合、ステータスコード422を返却すること", %{conn: conn} do
conn = post(conn, Routes.task_path(conn, :create), task: @invalid_attrs)
assert response(conn, :unprocessable_entity)
end
test "パラメータが空の場合、ステータスコード400を返すこと", %{conn: conn} do
conn = post(conn, Routes.task_path(conn, :create), @blank_params)
assert response(conn, :bad_request)
end
end
end