Julia web frameworkのGenieでTODOリストを作る（登録のみ。更新は未完成）

**この記事の内容は古いです。続編を書きましたのでそちらをご参照ください。**
[（続）Julia web frameworkのGenieでTODOリストを作る【2019年11月版】](https://qiita.com/yshutaro/items/8b5f1c179e132219d6b5)

# はじめに

最近、Juliaという言語が流行っていることを知りました。
https://twitter.com/hashtag/julia%E8%A8%80%E8%AA%9E?lang=ja
こんな記事もありました。
https://japan.zdnet.com/article/35124177/

Webアプリケーションフレームワークとかあるのかな？
Genieというのがありました。
http://genieframework.com/

ということで、GenieでよくあるサンプルのTODOリストを作ってみようと思いました。

# TODOリスト機能

今回作成するWebアプリケーションの機能は下記の通りです。

+ タスク一覧表示
+ タスク新規作成
+ タスク更新

この機能は下記Kotlin本のサンプルがわかりやすかったので名前など同じ形式にしています。
[Kotlin Webアプリケーション 新しいサーバサイドプログラミング](https://www.amazon.co.jp/Kotlin-Web%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3-%E6%96%B0%E3%81%97%E3%81%84%E3%82%B5%E3%83%BC%E3%83%90%E3%82%B5%E3%82%A4%E3%83%89%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-%E9%95%B7%E6%BE%A4-%E5%A4%AA%E9%83%8E/dp/4865940669)

# 準備

## Juliaインストール

インストールの詳細は公式ページや他の記事をご参照ください。すみません。
https://julialang.org/downloads/

私の環境はMacで、公式ページから.dmgファイルダウンロードではなく下記でインストールしました。

```shell
$ brew cask install julia
```

```shell
$ julia --version
julia version 1.0.1
```

現時点（2018/11/10）で公式ページでの最新バージョンは`v1.0.2`です。Homebrew版は`v1.0.1`のようです。（これを書いている途中でアップグレードしたので途中の記述がv1.0.0のままになっているところがあるかもしれません。）

# 新規アプリケーション作成

一旦アプリケーションを作成するディレクトリに移動してJuliaを起動します。

```shell
$ cd mywork # アプリケーションを作成するディレクトリに移動します。
$ julia # Julia起動
               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.0.1 (2018-09-29)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia> 
```
Genie取得。
[Genieの公式ページ](http://genieframework.com/)には書いていないのですが、`using Pkg` を実行しておかないとエラーが出ます。

```julia
julia> Pkg.clone("https://github.com/essenciary/Genie.jl") # Get Genie
ERROR: UndefVarError: Pkg not defined
```

```julia
julia> using Pkg

julia> Pkg.clone("https://github.com/essenciary/Genie.jl") # Get Genie
┌ Warning: Pkg.clone is only kept for legacy CI script reasons, please use `add`
└ @ Pkg.API /Users/osx/buildbot/slave/package_osx64/build/usr/share/julia/stdlib/v1.0/Pkg/src/API.jl:445
  Updating registry at `~/.julia/registries/General`
（略）
Building MbedTLS ──→ `~/.julia/packages/MbedTLS/mkHpa/deps/build.log`

julia> using Genie
julia>
```

アプリケーションを生成します。ここでは名前を`todolist`にしています。

```julia
julia> Genie.REPL.new_app("todolist")
（略）
[2018-10-07 02:38:52|info]: Starting your brand new Genie app - hang tight!
 _____         _
|   __|___ ___|_|___
|  |  | -_|   | | -_|
|_____|___|_|_|_|___|


Starting Genie in >> DEV << mode 
（略）
[2018-10-07 02:39:20|info]: Web server running at 127.0.0.1:8000

genie> 
```

ここで`Web server running at 127.0.0.1:8000`と表示されているので、ブラウザで
http://localhost:8000/
にアクセしてみます。
![スクリーンショット 2018-10-07 2.46.45.png](https://qiita-image-store.s3.amazonaws.com/0/66839/22ecdf63-1d06-026a-dde3-6340bda3a11b.png)

起動しています！
プロンプトが`genie>`になっているので、このままにして別のターミナルを開きます。

Juliaを起動したディレクトリに移動します。

```shell
$ cd mywork
$ ls
todolist
```

アプリケーションが生成されています。この後はこのディレクトリ以下のファイルを修正していきます。

+ `genie`を間違って抜けてしまったときは、下記で立ち上がります。

```shell
$ cd todolist
$ ./bin/server
```

# 公式チュートリアル

下記にチュートリアルがありとても詳しいです。今回これを参考にしました。
https://github.com/essenciary/Genie.jl
（これを読めばこの後読まなくていいかも。）

+ アプリケーションを生成するとチュートリアルの`README.md`が出来るのですが、それを元にサンプルを作成していたらエラーが出てしまいました。上記URLはIssueのやりとりでの修正も反映しているので上記を参照した方がよいです。

# ディレクトリ構成

`Genie`のルールでは`app/resources/`ディレクトリの下に機能のディレクトリ（小文字）を作成し、その下にコントローラーやビューを置くようです。

```
app/resources/
└── tasks
    ├── TasksController.jl
    └── views
        └── tasks.jl.html
```

これは`>genie`プロンプトで下記のようにコマンドを実行すると自動的にコントローラーまで作成されます。
（公式チュートリアルの`books`機能の例です）

```julia
genie> Genie.REPL.new_controller("Books")
[info]: New controller created at app/resources/books/BooksController.jl
```

今回は勉強のため手動で作成していきます。

# 機能作成（DBなしのサンプル表示まで）

## タスク一覧表示

### コントローラーの作成

`TasksController.jl`を作成します。ダミーのタスクを設定します。

```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
```

### ビューの作成

`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>
$(task.content)
</li>"
      else
      "<li>$(task.content)</li>"
      end
    end
  end
 %>
</ul>
```

+ routeの設定

`routes.jl`にタスク一表示用ルートを追加します。

```config/routes.jl
using Genie.Router
using TasksController # 追加

route("/") do
  serve_static_file("/welcome.html")
end

# 追加（タスク表示）
route("/tasks", TasksController.index)
```

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

![スクリーンショット 2018-10-30 23.04.27.png](https://qiita-image-store.s3.amazonaws.com/0/66839/4fc22b9a-6931-ce60-de4a-7b0d858907eb.png)

表示されました！

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

# 機能作成（表示・新規作成・更新）

## パッケージ追加

`SearchLight`というORMパッケージを追加します。
`pkg>`のプロンプトにするには、`julia>`プロンプトで`]`を入力します。

```julia
julia> ] # ←"]"を入力すると下記のプロンプトになります。
(v1.0) pkg>
```

`pkg>`プロンプトを抜けるときは`Ctrl-C`です。

+ 下記のコマンドで`SearchLight`を追加します。

```julia
(v1.0) pkg> add https://github.com/essenciary/SearchLight.jl
  Updating registry at `~/.julia/registries/General`
  Updating git-repo `https://github.com/JuliaRegistries/General.git`
（略）
  Building SQLite ──────────→ `~/.julia/packages/SQLite/xWe8d/deps/build.log`
  Building MySQL ───────────→ `~/.julia/packages/MySQL/owtkt/deps/build.log`

(v1.0) pkg>
```


## データベースセットアップ

### database.yml

`config`ディレクトリの下に`database.yml`というデータベース設定ファイルがあらかじめあるのでそれを編集します。

```config/database.yml
env: dev

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
[ Info: Precompiling SearchLight [340e8cb6-72eb-11e8-37ce-c97ebeb32050]

julia> SearchLight.Configuration.load_db_connection()
Dict{String,Any} with 0 entries
```

ん？`0 entries`? チュートリアルと違う。ソースコード（`SearchLight.jl/src/Configuration.jl`）を見るとパスを引数に設定しているようなので確認してみます。

```julia
julia> joinpath(SearchLight.CONFIG_PATH, SearchLight.SEARCHLIGHT_DB_CONFIG_FILE_NAME)
"<$HOME>/mywork/config/database.yml"
```

`config`ディレクトリのパスがおかしい。（本当は`<$HOME>/mywork/todolist/config/database.yml`。）

パッケージ追加するときに`todolist`ディレクトリに移動していなかったかも。
もう一度、パッケージ追加してみます。

```shell
$ cd mywork/todolist
$ julia
```

```julia
julia> ]
(v1.0) pkg> add https://github.com/essenciary/SearchLight.jl

julia> using SearchLight
julia> joinpath(SearchLight.CONFIG_PATH, SearchLight.SEARCHLIGHT_DB_CONFIG_FILE_NAME)
"<$HOME>/mywork/todolist/config/database.yml"
```

お、正しくなった。
もう一度、設定読み込み。

```julia
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接続

```julia
julia> SearchLight.Configuration.load_db_connection() |> SearchLight.Database.connect!
[ Info: Precompiling SQLite [0aa819cd-b072-5ff4-a722-6bc24af294d9]
WARNING: could not import Base.lcfirst into LegacyStrings
WARNING: could not import Base.next into LegacyStrings
WARNING: could not import Base.rsearch into LegacyStrings
WARNING: could not import Base.search into LegacyStrings
WARNING: could not import Base.ucfirst into LegacyStrings
[ Info: Precompiling IterableTables [1c8ee90f-4401-5389-894e-7a04a3dc0f4d]
SQLite.DB("db/tasks.sqlite")
```

`WARNING`がたくさん出て心配ですが、`tasks.sqlite`は出来ました。

```shell
$ cd db 
$ ls
migrations   seeds        tasks.sqlite
```

### DB migrations

`SearchLight`にはmigrations機能があるようです。RailsやLaravelみたいで便利ですね。
`db_init()`を実行します。

```julia
julia> SearchLight.db_init()
[info | SearchLight.Loggers]: SQL QUERY: CREATE TABLE `schema_migrations` (
    `version` varchar(30) NOT NULL DEFAULT '',
    PRIMARY KEY (`version`)
  )
┌ Warning: `SQLite.query(db, sql)` will return an `SQLite.Query` object in the future; to materialize a resultset, do `DataFrame(SQLite.query(db, sql))` instead
│   caller = ip:0x0
└ @ Core :-1
┌ Warning: `SQLite.Source(db, sql)` is deprecated in favor of `SQLite.Query(db, sql)` which executes a query and returns a row iterator
│   caller = ip:0x0
└ @ Core :-1
┌ Warning: In the future DataFrame constructor called with a `DataFrame` argument will return a copy. Use `convert(DataFrame, df)` to avoid copying if `df` is a `DataFrame`.
│   caller = |>(::DataFrames.DataFrame, ::Type) at operators.jl:813
└ @ Base ./operators.jl:813
  5.415232 seconds (6.16 M allocations: 303.549 MiB, 5.52% gc time)
[info | SearchLight.Loggers]: Created table schema_migrations
true
```

## モデルの作成

これは手動作成は大変そうなのでコマンドを実行します。コマンドは上記に引き続き行います。
`Task`というモデルを作成します。

```julia
julia> SearchLight.Generator.new_resource("Task")
[info | SearchLight.Loggers]: New model created at <$HOME>/todolist/app/resources/tasks/Tasks.jl
[info | SearchLight.Loggers]: New table migration created at <$HOME>/todolist/db/migrations/2018110113054822_create_table_tasks.jl
[info | SearchLight.Loggers]: New validator created at <$HOME>/todolist/app/resources/tasks/TasksValidator.jl
[info | SearchLight.Loggers]: New unit test created at <$HOME>/todolist/test/unit/tasks_test.jl
[warn | SearchLight.Loggers]: Can't write to app info
```

### create_tableファイルの修正

自動で作成された`*_create_table_tasks.jl`ファイルを修正します。

```db/migrations/自動生成_create_table_tasks.jl
module CreateTableTasks

import SearchLight.Migrations: 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
```

チュートリアルの`Book`は`string`だけだったので、`bool`がいけるか心配です。
`SearchLight.Migration.status()`でステータスチェックを行います。

```julia
julia> SearchLight.Migration.status()
┌ Warning: `SQLite.query(db, sql)` will return an `SQLite.Query` object in the future; to materialize a resultset, do `DataFrame(SQLite.query(db, sql))` instead
│   caller = ip:0x0
└ @ Core :-1
┌ Warning: `SQLite.Source(db, sql)` is deprecated in favor of `SQLite.Query(db, sql)` which executes a query and returns a row iterator
│   caller = ip:0x0
└ @ Core :-1
|   |                  Module name & status  |
|   |                             File name  |
|---|----------------------------------------|
|   |                 CreateTableTasks: DOWN |
| 1 | 2018110113054822_create_table_tasks.jl |
```

`SearchLight.Migration.last_up()`でテーブルを作成します。

```julia
julia> SearchLight.Migration.last_up()
┌ Warning: `SQLite.query(db, sql)` will return an `SQLite.Query` object in the future; to materialize a resultset, do `DataFrame(SQLite.query(db, sql))` instead
│   caller = ip:0x0
└ @ Core :-1
┌ Warning: `SQLite.Source(db, sql)` is deprecated in favor of `SQLite.Query(db, sql)` which executes a query and returns a row iterator
│   caller = ip:0x0
└ @ Core :-1
[info | SearchLight.Loggers]: SQL QUERY: CREATE TABLE tasks (id INTEGER PRIMARY KEY , content VARCHAR , done BOOLEAN )
┌ Warning: `SQLite.query(db, sql)` will return an `SQLite.Query` object in the future; to materialize a resultset, do `DataFrame(SQLite.query(db, sql))` instead
│   caller = ip:0x0
└ @ Core :-1
┌ Warning: `SQLite.Source(db, sql)` is deprecated in favor of `SQLite.Query(db, sql)` which executes a query and returns a row iterator
│   caller = ip:0x0
└ @ Core :-1
  0.003359 seconds (686 allocations: 59.109 KiB)
[info | SearchLight.Loggers]: SQL QUERY: CREATE  INDEX tasks__idx_content ON tasks (content)
┌ Warning: `SQLite.query(db, sql)` will return an `SQLite.Query` object in the future; to materialize a resultset, do `DataFrame(SQLite.query(db, sql))` instead
│   caller = ip:0x0
└ @ Core :-1
┌ Warning: `SQLite.Source(db, sql)` is deprecated in favor of `SQLite.Query(db, sql)` which executes a query and returns a row iterator
│   caller = ip:0x0
└ @ Core :-1
  0.005106 seconds (689 allocations: 59.156 KiB)
┌ Warning: `SQLite.query(db, sql)` will return an `SQLite.Query` object in the future; to materialize a resultset, do `DataFrame(SQLite.query(db, sql))` instead
│   caller = ip:0x0
└ @ Core :-1
┌ Warning: `SQLite.Source(db, sql)` is deprecated in favor of `SQLite.Query(db, sql)` which executes a query and returns a row iterator
│   caller = ip:0x0
└ @ Core :-1
[info | SearchLight.Loggers]: Executed migration CreateTableTasks up
```

`CREATE TABLE tasks (id INTEGER PRIMARY KEY , content VARCHAR , done BOOLEAN )`
と出ているので`bool`で問題ないみたいです。（相変わらず`Warning`がたくさん出ますが。）

もう一度、`SearchLight.Migration.status()`でステータスチェックを行います。

```julia
julia> SearchLight.Migration.status()
┌ Warning: `SQLite.query(db, sql)` will return an `SQLite.Query` object in the future; to materialize a resultset, do `DataFrame(SQLite.query(db, sql))` instead
│   caller = ip:0x0
└ @ Core :-1
┌ Warning: `SQLite.Source(db, sql)` is deprecated in favor of `SQLite.Query(db, sql)` which executes a query and returns a row iterator
│   caller = ip:0x0
└ @ Core :-1
|   |                  Module name & status  |
|   |                             File name  |
|---|----------------------------------------|
|   |                   CreateTableTasks: UP |
| 1 | 2018110113054822_create_table_tasks.jl |
```

`CreateTableTasks`のstatusが`UP`になりました。

### モデルの修正

自動で作成された`app/resources/tasks/Tasks.jl`を修正します。
自動生成されたコメントは削除してもいいのですが、勉強なのでそのまま残しています。コメントで`#追加`とある行に追加しています。（### constructorのTaskの部分と、下の方のnewの部分です。）

```app/resources/tasks/Tasks.jl
module Tasks

using SearchLight, Nullables, SearchLight.Validation, TasksValidator

export Task

mutable struct Task <: AbstractModel
  ### INTERNALS
  _table_name::String
  _id::String
  _serializable::Vector{Symbol}

  ### FIELDS
  id::DbId
  content::String # 追加
  done::Bool      # 追加

  ### VALIDATION
  # validator::ModelValidator

  ### CALLBACKS
  # before_save::Function
  # after_save::Function
  # on_save::Function
  # on_find::Function
  # after_find::Function

  ### SCOPES
  # scopes::Dict{Symbol,Vector{SearchLight.SQLWhereEntity}}

  ### constructor
  Task(;
    ### FIELDS
    id = DbId(),
    content = "", # 追加
    done = false  # 追加


    ### VALIDATION
    # validator = ModelValidator([
    #   ValidationRule(:title, TasksValidator.not_empty)
    # ]), 

    ### CALLBACKS
    # before_save = (m::Todo) -> begin
    #   @info "Before save"
    # end,
    # after_save = (m::Todo) -> begin
    #   @info "After save"
    # end,
    # on_save = (m::Todo, field::Symbol, value::Any) -> begin
    #   @info "On save"
    # end,
    # on_find = (m::Todo, field::Symbol, value::Any) -> begin
    #   @info "On find"
    # end,
    # after_find = (m::Todo) -> begin
    #   @info "After find"
    # end,

    ### SCOPES
    # scopes = Dict{Symbol,Vector{SearchLight.SQLWhereEntity}}()

  ) = new("tasks", "id", Symbol[],                                                ### INTERNALS
          id, content, done                                                       ### FIELDS #追加
          # validator,                                                                  ### VALIDATION
          # before_save, after_save, on_save, on_find, after_find                       ### CALLBACKS
          # scopes                                                                      ### SCOPES
          )   
end

end
```

### Seeding

初期データを作成してみます。`app/resources/tasks/Tasks.jl`に次の`function`を追加します。最初にサンプルで埋め込んだものと同じだとわからなくなるので別のデータにします。

```app/resources/tasks/Tasks.jl
# 追加
function seed()
  SampleTasks = [
    ("塩を買う", true),
    ("簡易書留を出す", false),
    ("本を5冊読む", false)
  ]
  for task in SampleTasks
    Task(content = task[1], done = task[2]) |> SearchLight.save!
  end
end
```

`Julia`は配列のindexは1からなんですね。
追加した`seed`関数を実行します。

```julia
julia> ]
(v1.0) pkg> activate .
(todolist) pkg> # Ctrl-C
julia> using Genie
julia> Genie.REPL.load_app()
 _____         _
|   __|___ ___|_|___
|  |  | -_|   | | -_|
|_____|___|_|_|_|___|


Starting Genie in >> DEV << mode 

genie> using Tasks
[ Info: Precompiling Tasks [top-level]

genie> Tasks.seed()
ERROR: UndefVarError: DatabaseAdapter not defined
```

エラーが出ました。なかなかチュートリアル通りにはいかないです。
もう一度、`Genie`に入り直して下記を実行したところうまくいきました。

```shell
$ cd todolist
$ ./bin/server
```

※出力は一部省略しています。

```julia
genie> using SearchLight
genie> SearchLight.Configuration.load_db_connection()
genie> SearchLight.Configuration.load_db_connection() |> SearchLight.Database.connect!
genie> ]
(todolist) pkg> activate .
(todolist) pkg> (Ctrl-C)
genie> using Tasks
genie> Tasks.seed()
```

```shell
$ cd db
$ sqlite3 tasks.sqlite 
sqlite> select * from tasks;
1|塩を買う|1
2|簡易書留を出す|0
3|本を5冊読む|0
```

データが入りました。

### コントローラーの修正

サンプルが埋め込んであった`TasksController.jl`を修正します。

```app/resources/tasks/TasksController.jl
module TasksController

using Genie.Renderer
using SearchLight
using Tasks

function index()
  html!(:tasks, :tasks, tasks = SearchLight.all(Tasks.Task))
end

end
```

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

![スクリーンショット 2018-11-04 22.39.02.png](https://qiita-image-store.s3.amazonaws.com/0/66839/86279f8d-d4d9-e04f-4ac1-e8547a4f4de7.png)

表示されました！

+ `TasksController.jl`の`SearchLight.all(Tasks.Task)`の部分はチュートリアルでは`SearchLight.all(Book)`で動いたのですが、`Task`だけだとなぜかエラーになり試行錯誤で`Tasks.Task`にしたら動きました。

+ よくGenie再起動後に`UndefVarError(:DatabaseAdapter)`が出ることがあったのですが下記を再実行すると出なくなりました。（次の「抜けていた修正」を行ったところエラーは出なくなりました。）

```julia
genie> using SearchLight
genie> SearchLight.Configuration.load_db_connection() |> SearchLight.Database.connect!
```

## 抜けていた修正

GitHubのissueを上げたところ作者からご丁寧な返信があり、チュートリアルの一部を読み飛ばしていることが判明。（作者もREADMEをアップデートすると書いてくださいました。こんな質問にご対応いただき感謝です。）
その箇所とは`config/initializers/searchlight.jl`のコメントアウトを外せというもの。
ファイルの`#=`と`=#`を削除します。

```config/initializers/searchlight.jl
# Uncomment the code to enable SearchLight support

#=　←これを消す
using SearchLight, SearchLight.QueryBuilder
（略）
Core.eval(Genie.REPL, :(using SearchLight, SearchLight.Generator, SearchLight.Migration))
=#　←これを消す
```

`Genie`を再起動します。

## タスク新規作成

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

### サンプルデータ削除

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

```shell
$ cd db
$ sqlite3 tasks.sqlite
delete from tasks;
sqlite> select count(*) from tasks;
0
```
http://localhost:8000/tasks

![スクリーンショット 2018-11-10 18.57.19.png](https://qiita-image-store.s3.amazonaws.com/0/66839/99512281-2214-e29f-7a74-d245027158cd.png)


### ルートの追加

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

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

### コントローラーの修正

`TasksController.jl`に下記のfunctionを追加します。`create()`の処理は後で書きます。

```app/resources/tasks/TasksController.jl
function new()
  html!(:tasks, :new)
end

function create()
end
```

### 追加用ビューの作成

`new.jl.html`を作成します。

```app/resources/books/views/new.jl.html
<form action="$(Genie.Router.link_to(: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
が、`500 Internal Server Error`が表示されてしまいます。
~~実はチュートリアルも下記にアクセスすると同じエラーが出て先に進めませんでした。~~ チュートリアルは上記の「抜けていた修正」を行ったところエラーが消えました。（関係なさそうなので、まだ理由は分かっていません。）
http://localhost:8000/bgbooks/new

いろいろ試したところ、`<form>`タグの"method"を消したらエラーが出なくなりました。原因不明のままですが全然進まないので一旦デフォルトのGETで送信することにします。
`Flax`というテンプレートエンジンがこのhtmlを`Julia`に変換して`build/FlaxViews/`ディレクトリに格納するようですが、エラーが出た後`.jl`ファイルを見ると`Juila`の文法的におかしい変換になっていることがあり、それでエラーになってしまっている感じです。まだ`Flax`に不具合があるかもしれません。一旦htmlもシンプルにタグを少なくします。

`new.jl.html`の修正（methodを消してさらにシンプルにする）。

```app/resources/books/views/new.jl.html
<form action="$(Genie.Router.link_to(:create_task))">
  内容:<input type="text" name="task_content" /></br>
  <input type="submit" value="作成" />
</form>
```

再度、
http://localhost:8000/tasks/new
にアクセスします。

![スクリーンショット 2018-11-06 23.18.05.png](https://qiita-image-store.s3.amazonaws.com/0/66839/51ac20a1-5969-acfc-ac8e-ed498f3dc145.png)

無事？表示されました。
methodを消したので`routes.jl`のmethodをPOSTからGETに修正しておきます。
また、登録後のリダイレクトを一覧表示画面にしたいので、`/tasks`に`get_tasks`という名前を付けます。ついでに`tasks/new`にも名前を付けます。

```config/routes.jl
（略）
route("/tasks", TasksController.index, named = :get_tasks) # namedを追加
route("/tasks/new", TasksController.new, named = :new_task) # namedを追加
route("/tasks/create", TasksController.create, method = GET, named = :create_task) # GETに修正
```

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

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

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

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

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

![スクリーンショット 2018-11-06 23.55.48.png](https://qiita-image-store.s3.amazonaws.com/0/66839/b7e7438f-f5b3-c9bf-9db0-547ca9c581f9.png)

内容に適当に（GETなので短めの）文字列を入れて[作成]ボタンをクリックします。

![スクリーンショット 2018-11-06 23.55.56.png](https://qiita-image-store.s3.amazonaws.com/0/66839/6119f014-2195-3c8d-49d4-7bc70588e275.png)

一覧画面にリダイレクトされ入力した内容が表示されています！

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

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

![スクリーンショット 2018-11-07 0.09.59.png](https://qiita-image-store.s3.amazonaws.com/0/66839/e82e08e7-ad22-1e24-d153-212e133f3ddb.png)

## タスク更新（未完成です）

最後にタスクの更新を作ります。
チュートリアルには更新処理（update）に関するサンプルがなく、いろいろ試したのですがエラーが出まくって解決できなく挫けました。参考までに試した方法だけ載せておきます。

### ルートの追加

`routes.jl`に下記を追加します。（ここも一応GET）

```config/routes.jl
route("/tasks/update", TasksController.update, method = GET, named = :update_task)
```

### コントローラーの修正

`TasksController.jl`に下記のfunctionを追加します。

```app/resources/tasks/TasksController.jl
function update()
  Tasks.Task(id = @params(:task_id), content = @params(:task_content), done = @params:(:task_done)) |> save && redirect_to(:get_tasks)
end
```

### ビューの修正

タスク一覧に[完了]と[未完了に戻す]のリンクを付けました。

```app/resources/tasks/views/tasks.jl.html
<a href="$(Genie.Router.link_to(:new_task))">作成</a>
<ul>
<%
  if length(@vars(:tasks)) == 0
    "タスクがありません。"
  else
    @foreach(@vars(:tasks)) do task
      if task.done
      "<li>
$(task.content)
<a href=" * Genie.Router.link_to(:update_task) * "?task_id=" * string(get(task.id)) * "&task_done=false" * ">&nbsp未完了に戻す</a></li>"
      else
      "<li>$(task.content)<a href=" * Genie.Router.link_to(:update_task) * "?task_id=" * string(get(task.id)) * "&task_done=true" * ">&nbsp完了</a></li>"
      end
    end
  end
 %>
</ul>
```

下記はデータベースに直接完了済のデータを入れて表示した状態です。

![スクリーンショット 2018-11-10 22.34.40.png](https://qiita-image-store.s3.amazonaws.com/0/66839/ec1e4313-cbff-2000-9ea3-7d01b4890865.png)

ここの[完了]リンクをクリックすると下記のリンクにパラメータが飛んで更新するという仕組みを想定していました。
http://localhost:8000/tasks/update

### エラー

画面には「false」とだけ出るので、`genie>`のプロンプトで下記を試したところ`UndefVarError(:s)`というエラーで確かに`false`が返ってきました。

```julia
genie> using SearchLight
genie> using Tasks
genie> Tasks.Task(id = 2, done = true) |> save
[2018-11-10 22:42:42|warn]: UndefVarError(:s)
genie> false
```

`save`関数だとupdateしないと思い`SearchLight.jl`のソースを見ると`update_by_or_create!!`や`update_with!`などの関数が見つかったのでいろいろ試したのですが、エラーの解決方法分からず一旦断念しました。（まだ`Julia`自体がきちんと読めない・・・）

# 感想

公式チュートリアルを少し変形しただけですが、もとのサンプルにないことを書こうとすると`Julia`の基本を知る必要があり勉強になりました。`Genie`のエラーが出てもまだまだ情報も少なく、解決するまでずいぶん時間を要しました。
`Genie`のissueを検索したら同じようなエラーで質問している方もいてそこで解決したこともあります。私もどうしても解決できなくてissueで質問したところ作者からすぐにリプライが来ました。（GitHubのissueではあまり質問しないほうがいいという記事を後で見つけました。質問に親切にリプライしてくれた作者に感謝です。）
結果的に更新処理は未完成のままなのでもう少し勉強して解決したいと思います。
`Julia`自体だけでなく`Genie`ももっと流行って欲しいですね！

# 参考
2018/9/24に英語版ですが`Julia 1.0`に対応した書籍も発売になっています。Kindle版を買ってみたところ一通り基本文法は学べそうな感じです。
[Julia 1.0 Programming: Dynamic and high-performance programming to build fast scientific applications, 2nd Edition (English Edition)](https://www.amazon.co.jp/Julia-1-0-Programming-high-performance-applications-ebook/dp/B07GVN47MR/)