この記事は Crystal Advent Calendar 2016 の20日目の記事です。
普段は Golang を書いている事が多いので、 Golang の人になっていますが、暇を見つけては色々な言語にも触れています。今日はその一つである Crystal Language について軽く CLI ツールを作る方法を書こうと思います。
Crystal へのモチベーション
そもそも、Crystal に触れるきっかけは「コマンドラインツール作るのに毎回 Golang で書くのはちょっとダルい… でも、 Ruby や Python のような LL 言語もできれば使いたくない…」というのがきっかけです。
Crystal はおおよそ Ruby の Syntax で記述が可能ですし、ネイティブコードを吐き出すことも可能なので、自分にとって打ってつけでした。マクロも定義できるので、慣れてしまえばかなり効率よく書けるんではないかと思い、進行形で勉強しています。
Crystal の所感
今のところ『すごくよい』の一言です。
Crystal's Goal
- Have a syntax similar to Ruby (but compatibility with it is not a goal)
- Statically type-checked but without having to specify the type of variables or method arguments.
- Be able to call C code by writing bindings to it in Crystal.
- Have compile-time evaluation and generation of code, to avoid boilerplate code.
- Compile to efficient native code.
最高です。
Markdown to HTML
今回紹介するツールは、ある Markdown から HTML ファイルを出力するという簡単なツールとなります。データの受け取り方は下記の二つとして作成をします。
- ファイル名をコマンドから受け取る
- 標準入力で受け取る
ちなみに、今回作成したコードは GitHub に置いてあります。
プロジェクト作成
$ crystal init app md2html
$ cd md2html
実装
実装といっても、 Crystal が用意しているライブラリを使っているだけなので、何も難しいことはしていません。まず、プロジェクトの構成は下記のようになっています。
$ tree . ├── bin │ └── md2html.cr ├── LICENSE ├── README.md ├── shard.yml ├── spec │ ├── md2html_spec.cr │ └── spec_helper.cr └── src ├── md2html │ └── version.cr └── md2html.cr
bin/md2html.cr は User Interface を提供しているアプリケーションレイヤーで、 src 配下のファイルが実際のビジネスロジックレイヤーです。(ただし、本当に標準のライブラリを使っているだけです)
src/md2html.cr
凝ったことをする場合はこのファイルをカスタマイズします。今は markdown.to_html を呼んでいるだけです。
require "./md2html/*" require "markdown" module Md2html class Md def initialize(@io : IO?) end def to_html(text : String?) io = @io return unless io return unless text io.puts Markdown.to_html(text) end end end
bin/md2html.cr
ユーザがコマンドで使うときの I/F 定義です。これが実行ファイルの元になります。
require "../src/md2html" require "option_parser" OptionParser.parse! do |parser| parser.banner = "Usage: md2html [OPTIONS] FILE" parser.on("-v", "--version", "print version") { puts Md2html::VERSION } parser.on("-h", "--help", "print this help") { puts parser } end md = Md2html::Md.new(STDOUT) if !STDIN.tty? md.to_html STDIN.gets_to_end exit 0 end if ARGV.empty? exit 1 end filename = ARGV.first if !File.exists?(filename) puts "No such file: " + filename exit 1 end md.to_html File.read(filename)
ここまでできたらビルドかそのまま実行して確認します。
# ビルド(リリース用) $ crystal build --release ./bin/md2html # 実行1 ## ファイル名を渡す crystal run のときは -- を使用して後続をコマンドライン引数として認識させる $ crystal run ./bin/md2html -- README.md # 実行2 ## 標準入力から渡す $ cat README.md | crystal run ./bin/md2html
これで標準出力に HTML 形式で出力されていると思います。
リソース
公式
https://crystal-lang.org/docscrystal-lang.org
Playground も用意されており、最高です。
Docker Container
Docker コンテナも存在しています。環境構築が面倒な方はこちらから。
https://hub.docker.com/r/crystallang/crystal/
For Vimmer!
Vimmer 用のプラグインです。是非、導入しましょう。
おわりに
2017年はさらに Crystal を使っていこうと思います。東京で勉強会とかあれば是非誘ってもらえると飛んで行きます。