Open7

Amber のプロジェクトを Scratch で作って Hello World するまで

373.3373.3

※コンソール出力はホームディレクトリ名やら個人名が出てる部分を削ってます

なんとなく brew install amber したくなかったので。
shards は根本的にプロジェクト単位でしか導入できないって思想がとても良いと思ったので、 Amber に対しても「所詮お前は単なる shard の一つなんだぞ。 Homebrew とは何事だ」という気持ちで向き合いたかった。

Amber のドキュメントの From Scratch を写経するだけの作業。

先に結論を書いておくと、素直に brew install amber した方が良い。

373.3373.3

crystal は 1.0.0、amber は入っていない状態。

$ crystal -v                                                                                                                                                                           ✘ 127
Crystal 1.0.0 (2021-03-22)

LLVM: 9.0.1
Default target: x86_64-apple-macosx

$ amber -v
zsh: command not found: amber

何はともあれ crystal init app する。
(本筋と関係ないが、crystal initlib も引数に取れるのがわかった。shard を自分で作るときとかに使うみたい。違いはまだわかってないので後に調べる。)

$ crystal init app myapp-amber
    create  myapp-amber/.gitignore
    create  myapp-amber/.editorconfig
    create  myapp-amber/LICENSE
    create  myapp-amber/README.md
    create  myapp-amber/.travis.yml
    create  myapp-amber/shard.yml
    create  myapp-amber/src/myapp-amber.cr
    create  myapp-amber/spec/spec_helper.cr
    create  myapp-amber/spec/myapp-amber_spec.cr
Initialized empty Git repository in myapp-amber/.git/

この時点でプレーンな Crystal アプリケーションのプロジェクトが出来上がった。

373.3373.3

Add Amber dependency をやっていく。

name: myapp-amber
version: 0.1.0

authors:
  - ryuhei4k9 <ryuhei.4k9@gmail.com>

targets:
  myapp-amber:
    main: src/myapp-amber.cr

crystal: 1.0.0

license: MIT

+dependencies:
+  amber:
+    github: amberframework/amber
+    version: 0.36.0

ドキュメントの例で記述されている Amber のバージョンが 0.7.2 とやたら古くてビビった。(2021/03/28 現在の最新バージョンは 0.36.0
書いたら shards install を叩く。

$ shards install
Resolving dependencies
Fetching https://github.com/amberframework/amber.git
Fetching https://github.com/amberframework/amber-router.git
Fetching https://github.com/crystal-lang/json_mapping.cr.git
Unable to satisfy the following requirements:

- `crystal (~> 0.35, >= 0.35.0)` required by `amber 0.36.0`
- `crystal (>= 0.35.0)` required by `amber_router 0.4.4`
- `crystal (~> 0.34, >= 0.34.0)` required by `json_mapping 0.1.0`
Failed to resolve dependencies, try updating incompatible shards or use --ignore-crystal-version as a workaround if no update is available.

Amber 0.36.0 は Crystal のバージョンを ~> 0.35 で指定しているので、1.0.0 では怒られてしまう。今回はとりあえず Hello World だけでもやりたいので、一旦 --ignore-crystal-version で進めていく。

$ shards install --ignore-crystal-version                                                                                                  
Resolving dependencies
Fetching https://github.com/amberframework/amber.git
Fetching https://github.com/amberframework/amber-router.git
Fetching https://github.com/crystal-lang/yaml_mapping.cr.git
Fetching https://github.com/crystal-loot/exception_page.git
Fetching https://github.com/mosop/teeplate.git
Fetching https://github.com/phoffer/inflector.cr.git
Fetching https://github.com/jeromegn/slang.git
Fetching https://github.com/luckyframework/shell-table.cr.git
Fetching https://github.com/stefanwille/crystal-redis.git
Fetching https://github.com/crystal-lang/crystal-sqlite3.git
Fetching https://github.com/crystal-lang/crystal-mysql.git
Fetching https://github.com/will/crystal-pg.git
Fetching https://github.com/amberframework/micrate.git
Fetching https://github.com/techmagister/liquid.cr.git
Fetching https://github.com/jeromegn/kilt.git
Fetching https://github.com/elorest/compiled_license.git
Fetching https://github.com/amberframework/cli.git
Fetching https://github.com/crystal-lang/json_mapping.cr.git
Fetching https://github.com/crystal-community/future.cr.git
Fetching https://github.com/ysbaddaden/pool.git
Fetching https://github.com/crystal-lang/crystal-db.git
Fetching https://github.com/amberframework/optarg.git
Fetching https://github.com/mosop/string_inflection.git
Fetching https://github.com/amberframework/callback.git
Installing amber_router (0.4.4)
Installing callback (0.7.1)
Installing string_inflection (0.2.1)
Installing optarg (0.8.0)
Installing cli (0.9.3)
Installing compiled_license (0.1.3)
Installing kilt (0.6.1)
Installing inflector (0.1.8)
Installing json_mapping (0.1.0)
Installing liquid (0.3.4)
Installing db (0.10.1)
Installing pg (0.23.1)
Installing mysql (0.13.0)
Installing sqlite3 (0.18.0)
Installing micrate (0.10.0)
Installing pool (0.2.3)
Installing redis (2.6.0)
Installing shell-table (0.9.2 at 078a04e)
Installing slang (1.7.2)
Installing future (0.1.0)
Installing teeplate (0.10.1)
Installing exception_page (0.1.4)
Installing yaml_mapping (0.1.0)
Installing amber (0.36.0)
Writing shard.lock
Shard "amber" may be incompatible with Crystal 1.0.0
Shard "callback" may be incompatible with Crystal 1.0.0
Shard "cli" may be incompatible with Crystal 1.0.0
Shard "compiled_license" may be incompatible with Crystal 1.0.0
Shard "exception_page" may be incompatible with Crystal 1.0.0
Shard "future" may be incompatible with Crystal 1.0.0
Shard "inflector" may be incompatible with Crystal 1.0.0
Shard "json_mapping" may be incompatible with Crystal 1.0.0
Shard "liquid" may be incompatible with Crystal 1.0.0
Shard "micrate" may be incompatible with Crystal 1.0.0
Shard "optarg" may be incompatible with Crystal 1.0.0
Shard "pool" may be incompatible with Crystal 1.0.0
Shard "redis" may be incompatible with Crystal 1.0.0
Shard "shell-table" may be incompatible with Crystal 1.0.0
Shard "slang" may be incompatible with Crystal 1.0.0
Shard "string_inflection" may be incompatible with Crystal 1.0.0
Shard "teeplate" may be incompatible with Crystal 1.0.0
Shard "yaml_mapping" may be incompatible with Crystal 1.0.0

依存 Shard が 1.0.0 未対応である旨の警告が山ほど出るが強い意志で無視していく。
ここで初めて Amber がプロジェクトにインストールされた。

373.3373.3

Create project structure をやっていく。

$ mkdir config
$ touch config/application.cr
$ touch config/routes.cr
$ mkdir -p src/controllers
$ touch src/controllers/application_controller.cr
$ touch src/myapp_amber.cr
config/application.cr
require "amber"
require "../src/controllers/**"
config/routes.cr
Amber::Server.configure do |app|
  pipeline :web do
    # plug PipeName.new
  end

  routes :web do
    # get "/", SomeController, :some_action
  end
end
src/controllers/application_controller.cr
class ApplicationController < Amber::Controller::Base
end
src/myapp_amber.cr
require "../config/*"

Amber::Server.start

この辺を手動で書く時点で、(そりゃそうなんだけど)素直に Amber CLI を入れた方が良いオーラが出まくっている。
が、一応最後まで頑張ってみようと思う。

373.3373.3

Build and run your project をやっていく。

$ shards build myapp-amber                                                                                                                 
Dependencies are satisfied
Building: myapp-amber

これで実行用のバイナリが生成されるので、そいつを実行する。

$ bin/myapp-amber
2021-03-27T18:05:23.181603Z   INFO - amber.server: Amber 0.36.0 serving application "Amber_app" at http://localhost:3000
2021-03-27T18:05:23.184427Z   INFO - amber.server: Server started in development.
2021-03-27T18:05:23.184435Z   INFO - amber.server: Startup Time 00:00:00.002844000

お!動いたっぽいので localhost にアクセスしてみると......

そういえば routes.cr に何も書いてないのでそれはそうだった。
というわけで、ルーティング、対応 Controller・View の実装をやっていく。

373.3373.3
config/routes.cr
# ルーティングの追加
Amber::Server.configure do |app|
  pipeline :web do
    # plug PipeName.new
  end

  routes :web do
    # get "/", SomeController, :some_action
+   get "/", HomeController, :index
  end
end
src/controllers/home_controller.cr
# Controller の実装
class HomeController < ApplicationController
  def index
    render("index.slang")
  end
end
$ mkdir -p src/views/home
$ touch src/views/home/index.slang
src/views/home/index.slang
doctype html
html
  head
    meta name="viewport" content="width=device-width,initial-scale=1.0"
    title myapp-amber
    css:
      h1 {color: red;}
      p {color: green;}
  body
    h1 Hello World!
    p Amber に入門してみた

ここまで書いて再び shards build myapp-amber

shards build myapp-amber
Dependencies are satisfied
Building: myapp-amber
Error target myapp-amber failed to compile:
Showing last frame. Use --error-trace for full trace.

There was a problem expanding macro 'embed'

Code in macro 'embed'

 4 | Slang.embed("src/views/layouts/application.slang", "__kilt_io__", )
     ^
Called macro defined in lib/slang/src/slang/macros.cr:2:3

 2 | macro embed(filename, io_name)

Which expanded to:

 > 1 | {{ run("slang/slang/process", "src/views/layouts/application.slang", "__kilt_io__") }}
          ^--
Error: Error executing run (exit code: 1): slang/slang/process src/views/layouts/application.slang __kilt_io__


stderr:

    Unhandled exception: Slang template: src/views/layouts/application.slang doesn't exist. (Exception)
      from raise<Exception>:NoReturn
      from raise<String>:NoReturn
      from __crystal_main
      from main

半端なくエラーが出たが、要は Slang template: src/views/layouts/application.slang doesn't exist. (Exception) とのこと。完全に忘れてたので書く。

$ mkdir -p src/views/layouts    
$ touch src/views/layouts/application.slang
src/views/layouts/application.slang
doctype html
html
  head
    meta name="viewport" content="width=device-width,initial-scale=1.0"
    title myapp-amber
    css:
      h1 {color: red;}
      p {color: green;}

  body
    == content

src/views/home/index.slangはレイアウトに埋め込まれる部分として修正。

slang
-doctype html
-html
-  head
-    meta name="viewport" content="width=device-width,initial-scale=1.0"
-    title myapp-amber
-    css:
-      h1 {color: red;}
-      p {color: green;}
-  body
+div
  h1 Hello World!
  p Amber に入門してみた

再びshards build myapp-amber

Dependencies are satisfied
Building: myapp-amber

無事にコンパイルされたので、もう一度実行する。

$ bin/myapp-amber
2021-03-27T18:34:18.292004Z   INFO - amber.server: Amber 0.36.0 serving application "Amber_app" at http://localhost:3000
2021-03-27T18:34:18.298438Z   INFO - amber.server: Server started in development.
2021-03-27T18:34:18.298447Z   INFO - amber.server: Startup Time 00:00:00.006453000

.......。UTF-8 にする。
バイナリ生成してるので、またshards buildするのを忘れずに。コンソール出力は省略。

src/views/layouts/application.slang
doctype html
html
  head
    meta name="viewport" content="width=device-width,initial-scale=1.0"
+   meta charset="utf-8"
    title myapp-amber
    css:
      h1 {color: red;}
      p {color: green;}

  body
    == content

お疲れ様でした。

373.3373.3

一番最後の Local CLI の項目が、一番やりたかったことだと今更ながらに気づく。
CLI をコンパイルして、 bin/amber でローカル環境で CLI を実行できる。
最初からこれでやればよかったが、勉強になったので良しとする。