19
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

(続)Julia web frameworkのGenieでTODOリストを作る【2019年11月版】

Last updated at Posted at 2019-11-06

はじめに

約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/
にアクセしてみます。

スクリーンショット 2019-11-06 13.00.14.png

起動しています!

ディレクトリ構成

別のターミナルを開いてディレクトリを確認します。

$ 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!だとエラーになります。

app/resources/tasks/TasksController.jl
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部分だけ書きます。

app/resources/tasks/views/tasks.jl.html
<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")

私は直接修正して強制上書きしました。

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になってしまったので起動の補足で再起動しました。
スクリーンショット 2019-10-30 14.17.54.png

表示されました!

次に、データベースをセットアップし、サンプル表示ではなく、タスクの表示・新規作成・更新を行えるように修正してきます。

機能作成(表示・新規作成・更新)

パッケージ追加

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を修正します。チュートリアルに倣い下記のようにします。

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

db/migrations/(自動生成)_create_table_tasks.jl
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を修正します。
Taskstructにフィールドを追加し、初期データ用関数を追加します。初期データは最初にサンプルで埋め込んだものと同じだとわからなくなるので別のデータにします。

app/resources/tasks/Tasks.jl
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 SearchLightusing Tasksも追加します。

app/resources/tasks/TasksController.jl
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
にアクセスします。

スクリーンショット 2019-10-31 10.46.07.png

表示されました!

タスク新規作成

データベースの内容が表示できたので、タスクを新規作成する機能を作ります。

サンプルデータ削除

一旦サンプルのデータは削除します。

$ pwd
$WORK/todolist/db
$ sqlite3 tasks.sqlite
sqlite> delete from tasks;
sqlite> select count(*) from tasks;
0

スクリーンショット 2019-10-31 10.52.26.png

ルートの追加

routes.jlに下記を追加します。

routes.jl
# 追加
route("/tasks/new", TasksController.new)
route("/tasks/create", TasksController.create, method = POST, named = :create_task)

コントローラーの修正

TasksController.jlに下記のfunctionを追加します。create()の処理は後で書きます。
(ここも前回と違いhtml!だとエラーになります。)

app/resources/tasks/TasksController.jl
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"

空ファイルができるのでエディタで編集します。

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

スクリーンショット 2019-10-31 11.08.55.png

表示されました!
前回はここでエラーが出て苦労したのですが今回はなにも問題ありませんでした。
Not foundになる場合は起動の補足で再起動します。

ルートの修正

登録後のリダイレクトを一覧表示画面にしたいので、/tasksget_tasksという名前を付けます。リンク用にtasks/newにも名前を付けます。

routes.jl
route("/tasks", TasksController.index, named = :get_tasks) # namedを追加
route("/tasks/new", TasksController.new, named = :new_task) # namedを追加

コントローラーの修正(create)

保留していたcreate()関数を修正します。タスクの初期は未完了なのでdoneはfalse固定です。
using Genie.Routerも追加します。

app/resources/tasks/TasksController.jl
using Genie.Router # 追加

# 修正
function create()
  Tasks.Task(content = @params(:task_content), done = false) |> save && redirect(:get_tasks)
end

http://localhost:8000/tasks/new
にアクセスし、内容に適当に文字列を入れて[作成]ボタンをクリックします。

スクリーンショット 2019-11-06 14.01.15.png スクリーンショット 2019-11-06 14.03.53.png

一覧画面にリダイレクトされ入力した内容が表示されています!
※実はここで500 Internal Server Errorが発生して調べたところバグぽっかったのでissueをあげたら速攻で直してくれました。

では、一覧画面から作成画面に遷移できるようにtasks.jl.htmlにリンクを付けます。(1行目に追加)

app/resources/tasks/views/tasks.jl.html
<a href="$(Genie.Router.linkto(:new_task))">作成</a>
(略)

http://localhost:8000/tasks
にアクセスします。

スクリーンショット 2019-11-06 14.17.47.png

タスク更新

ルートの追加

routes.jlに下記を追加します。

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()の処理は後ほど書きます。

app/resources/tasks/TasksController.jl
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のようになります。

app/resources/tasks/views/tasks.jl.html
<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を含めるように引数に指定します。

app/resources/tasks/views/edit.jl.html
<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 defined500 Internal Server Errorが発生するかもしれません。その場合は起動の補足で再起動します。

スクリーンショット 2019-11-06 11.25.23.png

タスク名がリンクになっていますので、クリックします。

スクリーンショット 2019-11-06 11.27.10.png

更新画面が表示されました!まだ更新処理を書いていないので追記していきます。

コントローラーの修正2

update()関数の処理を書きます。更新処理後にタスク一覧画面にリダイレクトするようにします。

app/resources/tasks/TasksController.jl
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)で例外が発生します。上記のような書き方にしましたがもっとシンプルに書ける方法もあると思います。

もう一度、タスク更新画面に遷移後、タスク名を修正して完了にチェックを入れて[更新]ボタンをクリックします。

スクリーンショット 2019-11-06 11.31.31.png スクリーンショット 2019-11-06 11.31.35.png

無事に更新されています!
タスクの完了を元に戻してみます。

スクリーンショット 2019-11-06 11.37.42.png スクリーンショット 2019-11-06 11.37.56.png

うまく動いています!

実装していないこと

  • バリデーション
    バリデーションを行っていないので空文字でタスク作成できてしまいます。その場合は選択もできないのでゴミになります。TasksValidator.jlが自動生成されるのでドキュメントやソースをみて書き方を調べようと思います。

  • 削除
    削除は実装していないので間違って作成すると消せません。

感想

今回は無事、更新処理まで書くことが出来ました。1年前と書き方が変わっていたりしましたが、エラーの発生は減っていたので早く進められたと思います。何かのご参考になれば幸いです。

19
22
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?