Life is TraversableOnce

programming / Java / Scala / Rust

テンプレートから開発プロジェクトをすばやく作るツール「Rig」

まだまだ開発段階ですが、Gitリポジトリにホストされている雛形からプロジェクトをすばやく造るためのツールRigを公開しました。

github.com

ほぼ、Scalagiter8のクローンで、その他に近しいものとしてはNode.jsのyo(yeoman)が挙げられると思います。

実装言語はRustです。わたし自身のRustの勉強をかねてのプロジェクトです。

特徴や導入方法など、日本語でまとめておきます。

(ちなみに、わたしがこのツールを公開する1日前に、まったく同じ用途のエレガントなツール↓がリリースされていて驚きました……!)

github.com


特徴

  • 特定のプラットフォーム・開発言語に依存していない
  • giter8テンプレートを流用できる(制限あり)

インストール

インストーラや実行可能形式での配布はしていません。CargoとRustコンパイラを使ってビルドする必要があります。rustupで一括してインストールするのが簡単でしょう。

ビルドにはCMakeが必要なので、事前にインストールしておいてください。

Cargoのインストールコマンドで、GitHubリポジトリを直接指定してビルドします。

$ cargo install --git https://github.com/lettenj61/rig

ビルドに成功すれば、パスが自動的に通っているはずです。

使い方

rigコマンドで起動します。基本的には、GitHubにホストされているリポジトリを、[オーナー]/[リポジトリ]の形式で引数に指定するだけです。

将来的にはGitHub以外のどんなgitリポジトリからでもテンプレートを導入できるようにしたいのですが、現在はGitHub上のリポジトリしか利用できません。

$ rig lettenj61/rig-test

完全なURLを指定することもできます。

$ rig https://github.com/letenj61/rig-test

giter8プロジェクトを流用する場合は、--giter8スイッチを使いましょう。

$ rig typesafehub/scala-sbt.g8 --giter8

プロキシ環境下で実行する場合、環境変数http_proxyにプロキシのURLを設定しておくことで、プロキシを参照します。 (くわしいプロキシURLの書き方は、gitのhttp.proxy設定方法などを検索してみてください)

このコマンドで、一時フォルダにリポジトリがクローンされます。

リポジトリを取得した後、テンプレートにあらかじめ定義されているデフォルト値に基づいて、プロジェクト名などの入力を促すプロンプトを表示します。

name [Sample Cargo Lib Project]:
author [Your name]:
greeting [Hello, World!]:
version [0.1.0]:

[..]に囲まれている部分がデフォルト値です。何も入力せずEnterキーを押すと、デフォルト値が使用されます。 入力されたパラメータは、後述のテンプレート書式の中に展開されます。

テンプレートとパラメータ

テンプレートはテキストファイルであればどんなものでも構いません。Rigは単純な文字列置換のみを行ないます。

テンプレートの中で、プロジェクトの生成時にパラメータとして受け取った値を展開したい部分に、プレースホルダを指定しておきます。

プレースホルダには、$記号に挟まれた$name$のような書式を用います。

たとえば、クラス名を受け取りたい場合:

class $class_name$ extends ...

プレースホルダには書式指定を含めることができ、生成される文字列を加工することができます。

class $class_name;Camel$ extends ...

このようなテンプレートに対し、パラメータclass_nameawesome toolが指定されると……

class AwesomeTool extends ...

このように先頭が大文字のキャメルケースに変換されて展開されます。

書式指定の方法は、いまのところgiter8に準拠しています。

デフォルト値

パラメータのデフォルト値は、テンプレート・プロジェクトのルートディレクトリに_rig.tomlというTomlファイルが参照されます。

Tomlファイルの構文についてはここでは触れませんが、Rigがパラメータとして認識するのはトップレベルの文字列、数値(整数・小数)、真偽値、日付だけで、配列とハッシュはスキップされます。

giter8テンプレートの場合は、Tomlファイルの変わりにdefault.propertiesというファイルが読み込まれます。


駆け足で紹介してみましたが、まだテストが不十分ですし、実装したい機能もたくさんあります。

引き続き、少しずつでも充実させていこうと思います。

Rustの資料集(日本語版)

勝手に引用しているので問題があればご連絡ください。


公式ドキュメント

公式ドキュメントのトップ

プログラミング言語Rust(trpl)日本語版

  • 通称The Book
  • 日本語版は少し古い版(1.6)をもとにしている?

Rust言語仕様

標準ライブラリのrustdoc

The Rustnomicon

  • 非安全(Unsafe)なRustコードを書くための上級者向けドキュメント

コンパイルエラー一覧

  • rustcのサブコマンドでもお馴染み、エラーコードと例文つきの索引

実践と例題

Rust Playground

  • ブラウザからRustコードを実行できるfiddle系のツール

Rust by Example日本語版

  • 言語の基礎を1歩ずつ確認できるチュートリアル
  • その場でコード例を編集、実行できる

規約とベストプラクティス

Rust Guildelines

ライブラリの紹介、解説

Crates.io

  • パッケージマネージャでありビルドツールであるCargoの公式サイト(Cargoのチュートリアル
  • CargoからRustのパッケージを検索&登録するサービスでもある

docs.rs

Awesome Rust

読み物

rust-prehistory

  • Rustを初めに設計・開発したGraydon Hoareさんの、当時の開発資料をまとめたリポジトリ。史料!

動画とスライド

Rust: Safe and Scalable Systems Programming

  • 英語。Rust自体の開発者であり、メジャーなライブラリをいくつもリリースしているAlex Crichtonさんの解説動画

The History of Rust

  • 英語。Ver. 1以前・以後のRustの歴史を4つの時期に分け、言語仕様の変遷などを解説した動画

Rust in Production

  • 英語。商用(Production)環境でRustを利用している実例を紹介
  • 新奇なものがProduction Readyになるまでの課題や、「何をもってProduction Readyとするのか?」という話もあり

ブログその他の記事

κeenさんのブログ

  • The Bookの翻訳などで活動されているκeenさんのブログ

錆にまみれたい

何かを書き留めるのは難しいですね~。 近頃はRustに入門しました。楽しい!

まだ「めっちゃメモリ管理してるぜ~」というプログラムは書いていませんが……。 むしろそのあたりを意識していないのでいいのかな?

2016年振り返り

今年は細かいところに集中できましたが、そのぶん大局を見落としていた気がします。

デモンストレーション用のすぐ動く何かをぱぱっと作りたいことが多いです。 春になるまでにそういう引き出しを増やしておきたいですね。

ギャラリーアンモナイト#1

昨日の記事ではできないことばかり並べてしまったので、今日は作ってみたものをご紹介。 Ammoniteですぐに使えるスクリプトを書きとめておくコーナーです。

カレントディレクトリを変更する

  /** bashのcdコマンドのように、現在地を移動する
    */
  object cd extends ammonite.ops.ImplicitOp[Unit] {
    import ammonite.repl.Ref
    import ammonite.ops._

    // JVMプロセスを起動したディレクトリを初期値とする
    val focus = Ref(cwd) // Ref[Path]型

    def apply(arg: Path): Unit = {
      if (exists! arg) {
        if (arg == root || arg.isDir) {
          focus() = arg
          println(focus())
        } else {
          println(s"$arg is not a directory.")
        }
      } else println(s"$arg does not exist.")
    }

    def ~ = apply(home)
  }

手軽に参照したい場合は下のようなメソッドを用意します。

  /** 暗黙的にカレントディレクトリへの参照を取得する
    */
  implicit def wd: Path = cd.focus()

cd! pathcd(path)でカレントディレクトリを移動します。 wdを定義しておけば、ls!の引数を省略して、よりbashのように動かすことができます。

ImplicitOp[Path]型にして直接パスを返させるのもよさそうですね。

古生物とのたたかい

最近AmmoniteのREPLライブラリをWindowsから使えないものかと取っ組み合っているので備忘のためにメモを残します。

依存性

試行した環境はこの通りです。

scalaVersion := "2.11.8"

libraryDependencies ++= Seq(
  "org.scalaj"  %% "scalaj-http"    % "2.3.0",
  "com.github.scopt" %% "scopt"     % "3.4.0",
  "com.lihaoyi" %% "upickle"        % "0.4.1",
  "com.lihaoyi" %% "ammonite-ops"   % "0.6.2",
  "com.lihaoyi" %% "ammonite-repl"  % "0.6.2" cross CrossVersion.full
)

課題

GithubIssueにも挙げられているようですが、Ammonite REPLはWindowsに対応していません。

起動できない

起動時の初期設定を行うpredef.scalaからターミナル操作クラスを変更して対応します。

repl.frontEnd() = ammonite.repl.frontend.FrontEnd.JLineWindows

スクリプトが読み込めない

load.moduleでファイルを読み取るとき、改行コードはLFを徹底しておかないと、シンタックスエラーになります。

解決できていないもの

  1. 例外を出力しようとしたとき、fansi.StrからNullPointerException
  2. プロンプトの色が固定できない
  3. grep!のハイライトが行全体に効いてしまう
  4. SIGINT(Ctrl + C)でスレッドが止められない
  5. stat!で取得できるファイルの属性は、Posix前提なので一部欠落する

とりあえず動かしているだけで楽しいですが、対応できるものが増えるといいですね〜。