はじめに
約1年前(2018年11月)に下記の記事を書きました。
Julia web frameworkのGenieでTODOリストを作る(登録のみ。更新は未完成)
しかし、Genie 自体の更新スピードが速く半年くらいで記事の内容が古くなってしまいました。
(参考)JuliaのWebフレームワークGenieを使おうとしたらハマった。
前回、更新処理も未完成だったので再度チャレンジしてみました。
アプリを起動したときに表示されたGenieのバーションはGenie v0.19.0
です。(後述する起動の補足で再起動したらv0.19.2
と表示されました。)v1.0
が出るまではまたすぐに古くなってしまうと思いますのでご注意ください。
公式ドキュメント
チュートリアル
Books
アプリケーションのチュートリアルはこちらにあります。今回もこれを参考にしました。
https://genieframework.github.io/Genie.jl/guides/Working_With_Genie_Apps.html
https://genieframework.github.io/Genie.jl/guides/Working_With_Genie_Apps_Intermediary_Topics.html
準備
Juliaバージョン
公式ドキュメントにはV1.0
以上が必要とあります。
Genie is compatible with Julia v1.0 and up.
私の環境では1.2.0
を使用しました。
$ julia --version
julia version 1.2.0
OSはMacです。
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.15
BuildVersion: 19A603
Genieパッケージインストール
Juliaを起動し、Juliaモードで]
を入力してパッケージモードになります
$ julia
_
_ _ _(_)_ | Documentation: https://docs.julialang.org
(_) | (_) (_) |
_ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 1.2.0 (2019-08-20)
_/ |\__'_|_|_|\__'_| | Official https://julialang.org/ release
|__/ |
julia> ] # 右角括弧を入力してパッケージモードになります。
パッケージモードで下記のコマンドを入力します。
(v1.2) pkg> add Genie
(略)
(v1.2) pkg> # Backspaceでパッケージモードを抜けます。
julia>
新規アプリケーション作成
アプリケーションを作成するディレクトリ(以下$WORK
)に移動してJuliaを起動します。
$ cd $WORK # アプリケーションを作成するディレクトリに移動します。
$ julia # Julia起動
アプリケーションを生成します。前回同様ここでは名前をtodolist
にしています。
julia> using Genie
julia> Genie.newapp("todolist")
(略)
_____ _
| __|___ ___|_|___
| | | -_| | | -_|
|_____|___|_|_|_|___|
| Web: https://genieframework.com
| GitHub: https://github.com/genieframework/Genie.jl
| Docs: https://genieframework.github.io/Genie.jl
| Gitter: https://gitter.im/essenciary/Genie.jl
| Twitter: https://twitter.com/GenieMVC
Genie v0.19.0
Active env: DEV
Web Server starting at http://0.0.0.0:8000
Web Server running at http://0.0.0.0:8000
julia>
起動したようです。前回はプロンプトがgenie>
でしたが今回はjulia>
のままになっています。
ブラウザで
http://localhost:8000/
にアクセしてみます。
起動しています!
ディレクトリ構成
別のターミナルを開いてディレクトリを確認します。
$ cd $WORK
$ ls
todolist
アプリケーションのtodolist
ディレクトリが出来ています。アプリケーションディレクトリに移動します。
$ cd todolist
$ ls -1
Manifest.toml
Project.toml
bin
bootstrap.jl
config
genie.jl
public
routes.jl
src
前回下記のように書きました。
Genieのルールではapp/resources/ディレクトリの下に機能のディレクトリ(小文字)を作成し、その下にコントローラーやビューを置くようです。
存在したはずのapp
ディレクトリが見当たらないなと思ったらドキュメントに補足がありました。
When creating a default Genie app, the app/ folder might be missing. This will be automatically created the first time you add a resource via Genie’s generators.
(Google翻訳)
デフォルトのGenieアプリを作成するときに、app /フォルダーが欠落している可能性があります。 これは、Genieのジェネレーターを介してリソースを初めて追加したときに自動的に作成されます。
どうやらディレクトリ構成は変わっていないようです。
上記の補足もあるのでGenieのコマンドを使ってコントローラーから作成していきます。
起動の補足
Genieを間違って抜けてしまったときは、アプリケーションディレクトリに移動し、bin/repl
を実行後up()
関数を実行します。(bin/server
だけでも起動しますが、julia>
のプロンプトになりませんでした。)
$ cd $WORK/todolist
$ bin/repl
_____ _
| __|___ ___|_|___
| | | -_| | | -_|
|_____|___|_|_|_|___|
| Web: https://genieframework.com
| GitHub: https://github.com/genieframework/Genie.jl
| Docs: https://genieframework.github.io/Genie.jl
| Gitter: https://gitter.im/essenciary/Genie.jl
| Twitter: https://twitter.com/GenieMVC
Genie v0.19.2
Active env: DEV
Ready!
julia> up()
Web Server starting at http://0.0.0.0:8000
Web Server running at http://0.0.0.0:8000
Dict{Symbol,Task} with 1 entry:
:ws => Task (runnable) @0x000000011243ed10
julia>
明示的に終了させる場合はexit()
関数を実行します。
julia> exit()
機能作成(DBなしのサンプル表示まで)
タスク一覧表示
コントローラーの作成
アプリケーション作成後、または上記起動の補足で起動後のjulia>
プロンプトの状態でGenie.newcontroller()
を実行します。
julia> Genie.newcontroller("Tasks")
[ Info: (日時) New controller created at ./app/resources/tasks/TasksController.jl
ディレクトリを確認するとtodolist
の下にapp
ディレクトリが作成され、コントローラーを含むいくつかのファイルが出来ています。
$ pwd
$WORK/todolist/app
$ tree
.
├── helpers
│ ├── ValidationHelper.jl
│ └── ViewHelper.jl
├── layouts
│ └── app.jl.html
└── resources
└── tasks
└── TasksController.jl
TasksController.jl
を修正し、ダミーのタスクを設定します。
注:structとTaskの間にはスペースがありますがQiitaの表示ではくっついてしまっています。
前回index
関数のhtml!
だった所はhtml
になっています。html!
だとエラーになります。
module TasksController
using Genie.Renderer
struct Task
id::Int
content::String
done::Bool
end
const SampleTasks = Task[
Task(1,"歯医者に行く",false),
Task(2,"はがきを出す",true),
Task(3,"定期券を買う",false),
]
function index()
html(:tasks, :tasks, tasks = SampleTasks)
end
end
ビューの作成
コントローラーを作成したjulia>
プロンプトの状態で下記を実行します。
julia> mkdir(joinpath("app", "resources", "tasks", "views"))
"app/resources/tasks/views"
julia> touch(joinpath("app", "resources", "tasks", "views", "tasks.jl.html"))
"app/resources/tasks/views/tasks.jl.html"
$WORK/app/resources/tasks/views
ディレクトリにtasks.jl.html
が作られたのでこれを修正します。
app/layouts/app.jl.html
に全体のテンプレートがあるので、body部分だけ書きます。
<ul>
<%
if length(@vars(:tasks)) == 0
"タスクがありません。"
else
@foreach(@vars(:tasks)) do task
if task.done
"<li><s>$(task.content)</s></li>"
else
"<li>$(task.content)</li>"
end
end
end
%>
</ul>
routeの設定
routes.jlにタスク一表示用ルートを追加します。(前回はconfig
ディレクトリの下にありましたが今回はアプリケーションディレクトリの直下に存在しました。)
エディタ(vim)で直接編集しようとしたところパーミッションが読み取り専用になっていました。(-r--r--r--)
チュートリアルではエディタによる直接修正または下記のコマンドを実行とあります。
julia> edit("routes.jl")
私は直接修正して強制上書きしました。
using Genie.Router
using TasksController # 追加
route("/") do
serve_static_file("welcome.html")
end
# 追加(タスク表示)
route("/tasks", TasksController.index)
確認
http://localhost:8000/tasks
にアクセスします。
- 一旦
/tasks Not found
になってしまったので起動の補足で再起動しました。
表示されました!
次に、データベースをセットアップし、サンプル表示ではなく、タスクの表示・新規作成・更新を行えるように修正してきます。
機能作成(表示・新規作成・更新)
パッケージ追加
SearchLight
というORMパッケージを追加します。上記に引き続きjulia>
プロンプトでパッケージモードになります。
julia> ] # 右角括弧を入力してパッケージモードになります。
(todolist) pkg>
プロンプトにアプリケーション名が付いた(todolist) pkg>
になります。アプリケーションのトップディレクトリにProject.toml
というファイルがありこれでパッケージを管理しているようです。
(todolist) pkg> add SearchLight
(todolist) pkg> # Backspaceでパッケージモードを抜けます。
julia>
データベースセットアップ
dbディレクトリ作成
julia>
プロンプトで下記を実行します。
julia> Genie.Generator.db_support()
実行後、アプリケーションディレクトリにdb
ディレクトリが作成されます。
$ pwd
$WORK/todolist/db
$ tree
.
├── connection.yml
├── migrations
└── seeds
2 directories, 1 file
DB接続設定
db/connection.yml
を修正します。チュートリアルに倣い下記のようにします。
env: ENV["GENIE_ENV"]
dev:
adapter: SQLite
database: db/tasks.sqlite
host:
username:
password:
port:
config:
prod:
adapter:
database:
host:
username:
password:
port:
config:
test:
adapter:
database:
host:
username:
password:
port:
config:
julia>
プロンプトに戻り下記を実行します。
julia> using SearchLight
(略)
julia> SearchLight.Configuration.load_db_connection()
Dict{String,Any} with 7 entries:
"host" => nothing
"password" => nothing
"config" => nothing
"username" => nothing
"port" => nothing
"database" => "db/tasks.sqlite"
"adapter" => "SQLite"
正しく設定が読み込めているようです。
DB接続+DB作成
続いて下記を実行します。
julia> SearchLight.Configuration.load_db_connection() |> SearchLight.Database.connect!
下記が表示されます。
SQLite.DB("db/tasks.sqlite")
ファイルの確認
$ pwd
$WORK/todolist/db
$ ls
connection.yml migrations seeds tasks.sqlite
tasks.sqlite
ファイルが出来ています。
DB Migration
続いてSearchLight.init()
を実行します。
julia> SearchLight.init()
┌ Info: (日時) CREATE TABLE `schema_migrations` (
│ `version` varchar(30) NOT NULL DEFAULT '',
│ PRIMARY KEY (`version`)
└ )
0.598814 seconds (1.31 M allocations: 65.443 MiB, 5.10% gc time)
[ Info: (日時) Created table schema_migrations
true
DBを確認してみます。
$ pwd
$WORK/todolist/db
$ sqlite3 tasks.sqlite
SQLite version 3.28.0 2019-04-15 14:49:49
Enter ".help" for usage hints.
sqlite> .schema
CREATE TABLE `schema_migrations` (
`version` varchar(30) NOT NULL DEFAULT '',
PRIMARY KEY (`version`)
);
モデルの作成
引き続き上記のjulia>
プロンプトで下記を実行し、Task
というモデルを作成します。
julia> SearchLight.Generator.newresource("Task")
[ Info: (日時) New model created at $WORK/todolist/app/resources/tasks/Tasks.jl
[ Info: (日時) New table migration created at $WORK/todolist/db/migrations/2019103006094007_create_table_tasks.jl
[ Info: (日時) New validator created at $WORK/todolist/app/resources/tasks/TasksValidator.jl
[ Info: (日時) New unit test created at $WORK/todolist/test/unit/tasks_test.jl
create_tableファイルの修正
自動で作成された*_create_table_tasks.jl
ファイルを修正します。
※注意:自動生成されたファイルの3行目のimport文が間違っています。Migrationsだとこの後エラーになります。(GitHubでissueをあげておきました。)
誤:import SearchLight.Migrations
正:mport SearchLight.Migration
module CreateTableTasks
import SearchLight.Migration: create_table, column, primary_key, add_index, drop_table
function up()
create_table(:tasks) do
[
primary_key()
column(:content, :string)
column(:done, :bool)
]
end
add_index(:tasks, :content)
end
function down()
drop_table(:tasks)
end
end
Migration実行
Migration実行前にSearchLight.Migration.status()
でステータスチェックを行います。
julia> SearchLight.Migration.status()
| | Module name & status |
| | File name |
|---|----------------------------------------|
| | CreateTableTasks: DOWN |
| 1 | 2019103006094007_create_table_tasks.jl |
DOWN
になっています。
SearchLight.Migration.last_up()
でMigration実行します。
julia> SearchLight.Migration.last_up()
[ Info: (日時) CREATE TABLE tasks (id INTEGER PRIMARY KEY , content VARCHAR , done BOOLEAN )
0.002634 seconds (35 allocations: 3.266 KiB)
[ Info: (日時) CREATE INDEX tasks__idx_content ON tasks (content)
0.001000 seconds (35 allocations: 3.266 KiB)
[ Info: (日時) Executed migration CreateTableTasks up
再度ステータスチェックを行います。
julia> SearchLight.Migration.status()
| | Module name & status |
| | File name |
|---|----------------------------------------|
| | CreateTableTasks: UP |
| 1 | 2019103006094007_create_table_tasks.jl |
UP
になっています。
DBの内容を直接確認してみます。
sqlite3 tasks.sqlite
SQLite version 3.28.0 2019-04-15 14:49:49
Enter ".help" for usage hints.
sqlite> .schema
CREATE TABLE `schema_migrations` (
`version` varchar(30) NOT NULL DEFAULT '',
PRIMARY KEY (`version`)
);
CREATE TABLE tasks (id INTEGER PRIMARY KEY , content VARCHAR , done BOOLEAN );
CREATE INDEX tasks__idx_content ON tasks (content);
テーブルとインデックスが作成されています。
モデルの修正
自動で作成されたapp/resources/tasks/Tasks.jl
を修正します。
Task
structにフィールドを追加し、初期データ用関数を追加します。初期データは最初にサンプルで埋め込んだものと同じだとわからなくなるので別のデータにします。
module Tasks
using SearchLight
export Task
mutable struct Task <: AbstractModel
### INTERNALS
_table_name::String
_id::String
### FIELDS
id::DbId
content::String # 追加
done::Bool # 追加
### constructor
Task(;
### FIELDS
id = DbId(), # カンマ追加
content = "", # 追加
done = false # 追加
) = new("tasks", "id", ### INTERNALS
id, content, done ### FIELDS
)
end
function seed()
SampleTasks = [
("塩を買う", true),
("簡易書留を出す", false),
("本を5冊読む", false)
]
for task in SampleTasks
Task(content = task[1], done = task[2]) |> SearchLight.save!
end
end
end
seedの実行
チュートリアルによればDBの設定はアプリケーションの起動時に読み込むようなので、julia>
プロンプトでexit()
を実行してアプリケーションを一旦終了します。
julia> exit()
bin/repl
でアプリーションを起動しますが、まだup()
は実行しません。
$ bin/repl
(略)
julia>
次を実行し、seedを実行します。
julia> using Tasks
julia> Tasks.seed()
[ Info: (日時) INSERT INTO tasks ( "content", "done" ) VALUES ( '塩を買う', true )
0.056246 seconds (179.13 k allocations: 9.518 MiB, 13.86% gc time)
[ Info: (日時) ; SELECT CASE WHEN last_insert_rowid() = 0 THEN -1 ELSE last_insert_rowid() END AS id
0.148794 seconds (292.32 k allocations: 14.856 MiB, 8.55% gc time)
[ Info: (日時) SELECT "tasks"."id" AS "tasks_id", "tasks"."content" AS "tasks_content", "tasks"."done" AS "tasks_done" FROM "tasks" WHERE ("tasks"."id" = 1) ORDER BY tasks.id ASC LIMIT 1
0.106621 seconds (340.23 k allocations: 17.029 MiB)
[ Info: (日時) INSERT INTO tasks ( "content", "done" ) VALUES ( '簡易書留を出す', false )
0.002415 seconds (35 allocations: 3.266 KiB)
[ Info: (日時) ; SELECT CASE WHEN last_insert_rowid() = 0 THEN -1 ELSE last_insert_rowid() END AS id
0.000091 seconds (52 allocations: 3.922 KiB)
[ Info: (日時) SELECT "tasks"."id" AS "tasks_id", "tasks"."content" AS "tasks_content", "tasks"."done" AS "tasks_done" FROM "tasks" WHERE ("tasks"."id" = 2) ORDER BY tasks.id ASC LIMIT 1
0.000138 seconds (66 allocations: 4.656 KiB)
[ Info: (日時) INSERT INTO tasks ( "content", "done" ) VALUES ( '本を5冊読む', false )
0.001347 seconds (35 allocations: 3.266 KiB)
[ Info: (日時) ; SELECT CASE WHEN last_insert_rowid() = 0 THEN -1 ELSE last_insert_rowid() END AS id
0.000103 seconds (52 allocations: 3.922 KiB)
[ Info: (日時) SELECT "tasks"."id" AS "tasks_id", "tasks"."content" AS "tasks_content", "tasks"."done" AS "tasks_done" FROM "tasks" WHERE ("tasks"."id" = 3) ORDER BY tasks.id ASC LIMIT 1
0.000142 seconds (66 allocations: 4.656 KiB)
次のコマンドでデータの確認を行います。
julia> using SearchLight
julia> all(Tasks.Task)
3-element Array{Tasks.Task,1}:
Tasks.Task
| KEY | VALUE |
|-------------------|----------|
| content :: String | 塩を買う |
| done :: Bool | true |
| id :: DbId | 1 |
Tasks.Task
| KEY | VALUE |
|-------------------|----------------|
| content :: String | 簡易書留を出す |
| done :: Bool | false |
| id :: DbId | 2 |
Tasks.Task
| KEY | VALUE |
|-------------------|-------------|
| content :: String | 本を5冊読む |
| done :: Bool | false |
| id :: DbId | 3 |
※チュートリアルではall(Book)
だけで確認できましたが、all(Tasks)
としたところエラーとなりました。all(Tasks.Task)
だとうまくいきました。
直接DBを確認してみます。
$ pwd
$WORK/todolist/db
$ sqlite3 tasks.sqlite
(略)
sqlite> select * from tasks;
1|塩を買う|1
2|簡易書留を出す|0
3|本を5冊読む|0
データが入りました。
タスク表示
コントローラーの修正
サンプルが埋め込んであったTasksController.jl
を修正します。サンプルを削除し、SearchLight.all(Tasks.Task)
を返すようにします。using SearchLight
とusing Tasks
も追加します。
module TasksController
using Genie.Renderer
using SearchLight # 追加
using Tasks # 追加
struct Task
id::Int
content::String
done::Bool
end
function index()
html(:tasks, :tasks, tasks = SearchLight.all(Tasks.Task))
end
end
up()
を実行し起動します。
julia> up()
Web Server starting at http://0.0.0.0:8000
Web Server running at http://0.0.0.0:8000
Dict{Symbol,Core.Task} with 1 entry:
:ws => Task (runnable) @0x000000011b9a0fd0
http://localhost:8000/tasks
にアクセスします。
表示されました!
タスク新規作成
データベースの内容が表示できたので、タスクを新規作成する機能を作ります。
サンプルデータ削除
一旦サンプルのデータは削除します。
$ pwd
$WORK/todolist/db
$ sqlite3 tasks.sqlite
sqlite> delete from tasks;
sqlite> select count(*) from tasks;
0
ルートの追加
routes.jlに下記を追加します。
# 追加
route("/tasks/new", TasksController.new)
route("/tasks/create", TasksController.create, method = POST, named = :create_task)
コントローラーの修正
TasksController.jl
に下記のfunctionを追加します。create()の処理は後で書きます。
(ここも前回と違いhtml!
だとエラーになります。)
function new()
html(:tasks, :new)
end
function create()
end
新規登録用ビューの作成
new.jl.html
を作成します。
julia> touch("app/resources/tasks/views/new.jl.html")
"app/resources/tasks/views/new.jl.html"
空ファイルができるのでエディタで編集します。
<form action="$(Genie.Router.linkto(:create_task))" method="POST">
<div>
<label>
内容:<input type="text" name="task_content"/>
</label>
</div>
<div>
<input type="submit" value="作成" />
</div>
</form>
ここで一旦下記にアクセスしてみます。
http://localhost:8000/tasks/new
表示されました!
前回はここでエラーが出て苦労したのですが今回はなにも問題ありませんでした。
Not foundになる場合は起動の補足で再起動します。
ルートの修正
登録後のリダイレクトを一覧表示画面にしたいので、/tasks
にget_tasks
という名前を付けます。リンク用にtasks/new
にも名前を付けます。
route("/tasks", TasksController.index, named = :get_tasks) # namedを追加
route("/tasks/new", TasksController.new, named = :new_task) # namedを追加
コントローラーの修正(create)
保留していたcreate()
関数を修正します。タスクの初期は未完了なのでdoneはfalse固定です。
using Genie.Router
も追加します。
using Genie.Router # 追加
# 修正
function create()
Tasks.Task(content = @params(:task_content), done = false) |> save && redirect(:get_tasks)
end
http://localhost:8000/tasks/new
にアクセスし、内容に適当に文字列を入れて[作成]ボタンをクリックします。
一覧画面にリダイレクトされ入力した内容が表示されています!
※実はここで500 Internal Server Errorが発生して調べたところバグぽっかったのでissueをあげたら速攻で直してくれました。
では、一覧画面から作成画面に遷移できるようにtasks.jl.html
にリンクを付けます。(1行目に追加)
<a href="$(Genie.Router.linkto(:new_task))">作成</a>
(略)
http://localhost:8000/tasks
にアクセスします。
タスク更新
ルートの追加
routes.jl
に下記を追加します。
route("/tasks/:id::Int/edit", TasksController.edit, method = GET, named = :edit_task)
route("/tasks/:id::Int/update", TasksController.update, method = POST, named = :update_task)
id
の指定の方法はここを参考にしました。
https://github.com/GenieFramework/Genie.jl/blob/master/docs/documentation/12--Advanced_Routing_Techniques.md
コントローラーの修正
TasksController.jlに下記のfunctionを追加します。payload
関数でURLに含まれるid
を取得します。payload
関数のためusing Genie.Requests
も追加します。
update()
の処理は後ほど書きます。
using Genie.Requests # 追加
function edit()
id = payload(:id)
task = SearchLight.findoneby(Tasks.Task , SQLWhereExpression("id = ?", id))
html(:tasks, :edit, task = task)
end
function update()
end
タスク一覧ビューの修正
タスク一覧のタスクをtasks/edit
へのリンクにして、id
をURLに含めるようにしました。
※ linto
関数の引数にパラメータを入れると/tasks/1/edit
のようになります。
<a href="$(Genie.Router.linkto(:new_task))">作成</a>
<ul>
<%
if length(@vars(:tasks)) == 0
"タスクがありません。"
else
@foreach(@vars(:tasks)) do task
if task.done
"""<a href="$(Genie.Router.linkto(:edit_task, id = task.id))"><li><s>$(task.content)</s></li></a>"""
else
"""<a href="$(Genie.Router.linkto(:edit_task, id = task.id))"><li>$(task.content)</li></a>"""
end
end
end
%>
</ul>
タスク更新用ビューの作成
edit.jl.html
を作成します。
julia> touch("app/resources/tasks/views/edit.jl.html")
"app/resources/tasks/views/edit.jl.html"
空ファイルができるのでエディタで編集します。update_task
のURLにid
を含めるように引数に指定します。
<form action="$(Genie.Router.linkto(:update_task, id = (get(@vars(:task))).id))" method="POST">
<div>
<label>
内容:<input type="text" name="task_content" value="$((get(@vars(:task))).content)" />
</label>
</div>
<div>
<label>
<%
if (get(@vars(:task))).done == true
"""完了:<input type="checkbox" name="task_done" value="1" checked="checked" />"""
else
"""完了:<input type="checkbox" name="task_done" value="1" />"""
end
%>
</label>
</div>
<div>
<input type="hidden" name="task_id" value="1" />
</div>
<div>
<input type="submit" value="更新" />
</div>
</form>
ここで一旦確認のため
http://localhost:8000/tasks
にアクセスします。
ルートの追加なのでRoute named
edit_task is not defined
の500 Internal Server Error
が発生するかもしれません。その場合は起動の補足で再起動します。
タスク名がリンクになっていますので、クリックします。
更新画面が表示されました!まだ更新処理を書いていないので追記していきます。
コントローラーの修正2
update()
関数の処理を書きます。更新処理後にタスク一覧画面にリダイレクトするようにします。
function update()
id = payload(:id)
done = false
try
@params(:task_done)
done = true
catch
done = false
end
Tasks.Task(id = id, content = @params(:task_content), done = done) |> save && redirect(:get_tasks)
end
チェックボックスにチェックを入れないとパラメータが飛ばないので、@params(:task_done)
で例外が発生します。上記のような書き方にしましたがもっとシンプルに書ける方法もあると思います。
もう一度、タスク更新画面に遷移後、タスク名を修正して完了にチェックを入れて[更新]ボタンをクリックします。
無事に更新されています!
タスクの完了を元に戻してみます。
うまく動いています!
実装していないこと
-
バリデーション
バリデーションを行っていないので空文字でタスク作成できてしまいます。その場合は選択もできないのでゴミになります。TasksValidator.jl
が自動生成されるのでドキュメントやソースをみて書き方を調べようと思います。 -
削除
削除は実装していないので間違って作成すると消せません。
感想
今回は無事、更新処理まで書くことが出来ました。1年前と書き方が変わっていたりしましたが、エラーの発生は減っていたので早く進められたと思います。何かのご参考になれば幸いです。