JuliaのGenieを使ってDB操作をするところまで


### 別サイト
に書いていたのだけどサ終してしまったので移行した記事その2
2019/03/06のもの

### Julia
Juiliaという言語があるのを最近知ったのでサンプルに従って書いてみる

### Genie
Julia向けにGenieというwebフレームワークがあったのでそれを使ってDB接続するところまでをやっていく

## 環境構築
https://julialang.org/downloads/
からJuliaをダウンロード、インストール

```
$ julia --version
julia version 1.1.0
```
自分が入れた段階では1.1.0だった

### Genieインストール、アプリ作成
```
$ julia
julia> using Pkg
julia> Pkg.clone("https://github.com/essenciary/Genie.jl")
julia> using Genie
julia> Genie.REPL.new_app("app")
julia> ] ## ]をいれるとパッケージモードになる
pkg> add https://github.com/essenciary/SearchLight.jl  ##DB接続用にSearchLightをいれる
```
これで基本は完了。
./bin/serverなどで起動すると
http://localhost:8000/
でトップページが表示できました

### 実装
https://github.com/essenciary/Genie.jl
[Julia web frameworkのGenieでTODOリストを作る](https://qiita.com/yshutaro/items/16cb2e02655911fed83b "Julia web frameworkのGenieでTODOリストを作る")
公式ドキュメントや他の方がやられていたのを参考に実装していく
app/resources配下にコントローラやビューを作成する作法らしい

今回作成したファイル

- app/resources/tests/Tests.jl（生成は自動）
- app/resources/tests/TestsController.jl
- app/resources/tests/TestsValidator.jl（生成は自動）
- app/resources/tests/views/new.jl.html
- app/resources/tests/views/tests.jl.html


修正したファイル

- config/database.yml
- config/routes.jl

ソースは後述

### database設定
config/initializers/searchlight.jlのコメントを外す

```jl:config/initializers/searchlight.jl
using SearchLight, SearchLight.QueryBuilder

Core.eval(SearchLight, :(config.db_config_settings = SearchLight.Configuration.load_db_connection()))

SearchLight.Loggers.empty_log_queue()

if SearchLight.config.db_config_settings["adapter"] != nothing
  SearchLight.Database.setup_adapter()
  SearchLight.Database.connect()
  SearchLight.load_resources()
end
```

database.ymlに設定追加

```yml:database.yml
env: dev

dev:
  adapter: SQLite
  database: db/tests.sqlite
  host:
  username:
  password:
  port:
  config:
```

 DB接続、モデル作成

```
julia> SearchLight.Configuration.load_db_connection()
julia> SearchLight.Configuration.load_db_connection() |> SearchLight.Database.connect!
julia> SearchLight.db_init()
julia> SearchLight.Generator.new_resource("Test")
```

作成されたマイグレーションファイルに適当にカラムを設定

```jl:db/migrations/xxxxx_create_table_tests.jl
module CreateTableTests

import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table

function up()
  create_table(:tests) do
    [
      primary_key()
      column(:content, :string)
      column(:done, :bool)
    ]
  end

  add_index(:tests, :content)
end

function down()
  drop_table(:tests)
end

end
```

テーブル作成

```
julia> SearchLight.Migration.last_up()
```

モデルファイルもマイグレーションファイルと同様にカラム設定をいれる
seedファンクションにて初期データ用の処理も追加

```jl:app/resources/tests/Tests.jl
mutable struct Test <: AbstractModel
  ### INTERNALS
  _table_name::String
  _id::String
  _serializable::Vector{Symbol}

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

  ### constructor
  Test(;
    ### FIELDS
    id = DbId(),
    content = "",
    done = false

  ) = new("tests", "id", Symbol[],                                                ### INTERNALS
          id, content, done                                                             ### FIELDS
          )
end

function seed()
  SampleTests = [
    ("ほげ", true),
    ("ほげほげ", false),
    ("げほげほ", false)
  ]
  for test in SampleTests
    Test(content = test[1], done = test[2]) |> SearchLight.save!
  end
end
```

初期データ投入

```
julia> ]
pkg> activate .
pkg> # Ctrl-C
julia> using Genie
julia> Genie.REPL.load_app()
julia> using Tests
julia> Tests.seed()
```

### コントローラ、ビュー、ルートの修正
コントローラ作成->ビュー作成->ルート作成
みたいな感じで作成していきました
動かすたびにエラーでたり試行錯誤が多かったけど最終的に

- DBからselectしたものを表示
- DBにデータをinsert
- DBのデータをupdate

といったところができた

コントローラ

```jl:app/resources/tests/TestsController.jl
module TestsController

using Genie.Renderer
using Genie.Router
using SearchLight
using Tests

# トップ
function index()
  html!(:tests, :tests, tests = SearchLight.all(Tests.Test))
end

# 新規作成
function new()
  html!(:tests, :new)
end

# 新規登録
function create()
  # 作成
  Tests.Test(content = @params(:test_content), done = false) |> save
  redirect_to(:get_tests)
end

# 更新
function update()
  # 既存データのselect
  test = SearchLight.find_one_by!!(Tests.Test, :id, @params(:test_id))
  # リクエストパラメータ（なんかstringで受けるのでそのままbool判定できなかった）
  in_done = ifelse(@params(:test_done) == "true", true, false)
  # 更新処理
  Tests.Test(id = @params(:test_id), content = test.content, done = in_done) |> save!
  # トップにリダイレクト
  redirect_to(:get_tests)
end

end
```

ビュー

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

velocityみたいな感じで書ける

```html:app/resources/tests/views/tests.jl.html
<a href="$(Genie.Router.link_to(:new_test))">作成</a>
<ul>
<%
  if length(@vars(:tests)) == 0
    "データがありません。"
  else
    @foreach(@vars(:tests)) do test
      if test.done
      "<li>
$(test.content)
<a href=" * Genie.Router.link_to(:update_test) * "?test_id=" * string(get(test.id)) * "&test_done=false" * ">&nbsp falseに戻す</a></li>"
      else
      "<li>$(test.content)<a href=" * Genie.Router.link_to(:update_test) * "?test_id=" * string(get(test.id)) * "&test_done=true" * ">&nbsp trueにする</a></li>"
      end
    end
  end
 %>
</ul>
```

ルート

```jl:config/routes.jl
using Genie.Router
using TestsController

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

# トップページ
route("/tests", TestsController.index, named = :get_tests)
# 新規登録画面
route("/tests/new", TestsController.new, named = :new_test)
# 登録処理
route("/tests/create", TestsController.create, method = GET, named = :create_test)
# 更新処理
route("/tests/update", TestsController.update, method = GET, named = :update_test)
```

### 感想
コードは見やすく短くかけていい感じだと思う
ただまだよくわからないところも多く（サンプルの通りでも怪しいところがあったり）
エラーが起きたときにどう対処すればいいかがわからないことが多かった
同じソースでも起動し直したら動いたりするのも何故なのかもよくわからない
POSTメソッドでもなぜかエラーが出たり。。。