05. Shards

Ruby に RubyGems があるように、Crystal にも Shards というパケージ管理の仕組みが存在しています。Shards は Crystal 標準のパッケージマネージャとして採用されており、Shards 関連の操作に使用する shards コマンドも Crystal コンパイラに同梱されています。ですので、Shards を利用するための特別な準備は必要ありません。

Shards における個々のパッケージは shard (シャード)と呼ばれ、これは鉱物などの「欠片」や「破片」といった意味の単語です。Ruby や Python といった先行する言語と比べると充分ではないかもしれませんが、それでも GitHub などを利用して 2000 を超える shard が公開されています。

RubyGems と Shards の大きな違いは、パッケージのインストール先です。RubyGems は基本的にグローバルな Ruby の実行環境に対してパッケージをインストールする仕組みで、それをプロジェクト単位で個別管理するために別途 Bundler が登場しました。一方、Shards では導入パッケージをプロジェクト単位で管理することが前提となっています。

また、他言語のパッケージ管理手法と異なり、Shards には集中管理された公式のリポジトリが存在しません。ほとんどの shard は、git リポジトリとして(多くの場合 GitHub 上で)公開されています。このことは、shard を公開することへのハードルを大きく下げてくれていますが、一方で公開されている shard のクォリティ確保や、検索性の面では足かせにもなっています。

ただし、この原稿を執筆している2019年1月時点で Crystal がバージョン 1.0 に至っていないように、Shards の開発は現在も継続中です。現時点で課題となっている点も、今後のバージョンアップで改善されることを期待しましょう。

この章では、shard の使い方と作り方、および shard 作成において使用することになるテストの実行方法について簡単に説明します。

shard を使う

まずはインターネット上に公開されている shard を自分のプログラムから利用するための手順を見てみましょう。

プロジェクトの機能と名前を決める

前述の通り、Shards では導入パッケージの管理をプロジェクト単位でおこなうため、なにはともあれそのプロジェクトで実現したい機能とプロジェクトの名前を決めなければいけません。とはいえ、通常はやりたいことがあってプログラムを書き始めるはずですので、この辺りでつまずくこともそうないとは思います。

というわけで、実用的かどうかはさておき、ページタイトルと本文をパラメータとして受け取って最低限の HTML ソースを出力する、というだけのシンプルなコマンドラインツールを作ってみることにしましょう。

プロジェクト名は、shard として公開するのであればいくつかルール(後述)もありますが、そうでない場合にはそこまで深く考える必要はありません。今回はシンプル(simple)な HTML を返すアプリなので simple_html というプロジェクト名にすることにします。

simple_html コマンド利用イメージ
$ simple_html "Page Title" "Page body."
<html><head><title>Page Title</title></head><body><h1>Page Title</h1><p>Page body.</p></body></html>

プロジェクトひな形を作る

crystal コマンドにはソースコードのコンパイル以外にもいくつか便利な機能が実装されており、その中の1つにプロジェクトひな形の生成機能(crystal init コマンド)があります。

crystal init コマンドを実行する際には、プロジェクトの種類(app もしくは lib)とプロジェクト名を指定します。例えば、simple_html をアプリケーションとして実装する場合のひな形生成コマンドは crystal init app simple_html です。このコマンドを実行するとカレントディレクトリの直下にプロジェクト名と同じ名前の simple_html ディレクトリができ、その中に各種ひな形が自動生成されます。

simple_html アプリケーション用プロジェクトのひな形生成コマンド
$ crystal init app simple_html
プロジェクトひな形として生成されるファイル/ディレクトリ
.editorconfig

文字コードや改行コード、インデントなどの設定を異なるエディタ間で共通利用するための設定ファイル。詳細を知りたければ EditorConfig で検索。

.git/(ディレクトリ)

このプロジェクト用の初期化済み git リポジトリ。

.gitignore

git 管理除外ファイルの指定用。

.travis.yml

Travis CI を使用してテストやビルドを自動化するための設定ファイル。使いこなせば強力なツール。ただ、最初のうちはあまり気にしなくても良い。

LICENSE

このプロジェクトのライセンスを明示するためのテキストファイル。自分で使うだけのアプリケーションなら気にする必要はない。標準では MIT ライセンス前提の内容になっているので、必要に応じて適宜変更する。

README.md

Markdown フォーマットで書かれたこのプロジェクトの README ファイル。GitHub などを通じて公開する予定ならちゃんと書いた方が良い。

shard.yml

プロジェクトに関連したメタデータ(名前やバージョン、作者など)やプロジェクトが利用する shard などの情報を記述するための設定ファイル。

src/(ディレクトリ)

プログラムのソースディレクトリ。ソースコードはすべてこの配下に置く。

src/[プロジェクト名].cr

プロジェクトのメインソースファイル。プログラムのエントリポイントはここ。

spec/(ディレクトリ)

テスト用ディレクトリ。テスト関連のファイルはここへ置く。

spec/spec_helper.cr

テスト関連ファイルその1。あまり触らない方。

spec/[プロジェクト名]_spec.cr

テスト関連ファイルその2(spec ファイル)。実際のテスト内容を記述する方。

shard を探す

simple_html を実装するにあたり HTML をすべて手打ちすることもできますが、それはいささか面倒なので HTML を出力するのに便利な shard を探してみることにします。

Shards には公式のリポジトリがなく、shard の検索機能が提供されていません。そのため、標準ライブラリで提供されていない機能を使いたい場合に、その機能を提供する shard が存在するかどうかを調べるのが最初のハードルになります。

今のところ、この問題を解決する決定打は出てきていませんが、以下の2サイトは必要な shard を探す際の助けになってくれるはずです。

Awesome Crystal

https://github.com/veelenga/awesome-crystal
一定の条件を満たした自薦他薦の shard をカテゴリ分類してリスト化。リストの更新も人の手で管理されているため、限定的ではあるもののある程度の基準をクリアした shard が見つかる。

CrystalShards.xyz

http://crystalshards.xyz/
GitHub で公開されている shard のキーワード検索機能を提供。GitHub で公開されている shard を自動的に収集しているため玉石混こうではあるものの、Awesome Crystal より網羅的にマイナな shard も見つけられる。

試しに Awesome Crystal 内を「 html 」で検索してみると、HTML 構築専用の構文を提供してくれる html_builder という shard が見つかりました。早速これを使ってみることにしましょう。

使用する shard を指定する

プロジェクトディレクトリ内にある shard.yml ファイルは、shard 関係の設定を記述するファイルです。

アプリケーションとして初期化された shard.yml は以下のような内容になっています。

ひな形生成時の shard.yml
name: simple_html
version: 0.1.0

authors:
  - Your Git Name <your_mail@example.org>

targets:
  simple_html:
    main: src/simple_html.cr

crystal: 0.27.0

license: MIT

ここへ html_builder を指定するための設定を追加しましょう。大抵の場合 shard の README にはその shard を使用するために shard.yml へ追加すべき内容が紹介されています。

README に従って dependencies を追加した shard.yml はこのようになりました。

依存ライブラリの設定追加後
name: simple_html
version: 0.1.0

authors:
  - Your Git Name <your_mail@example.org>

targets:
  simple_html:
    main: src/simple_html.cr

crystal: 0.27.0

license: MIT

dependencies:
  html_builder:
    github: crystal-lang/html_builder

使いたい shard のバージョン指定する

2018年9月時点の html_builder はバージョン 0.2.2 が最新です。そのため、バージョン指定をせず shard をインストールした場合には最新のバージョン 0.2.2 がインストールされます。しかし、shard.yml では、インストールする shard のバージョンを指定することもできます。例えば、shard.yml での記述を以下のようにすると、旧バージョンの 0.2.1 をインストールできます。

バージョン指定付きの shard 設定
dependencies:
  html_builder:
    github: crystal-lang/html_builder
    version: 0.2.1

ここで指定する version には、特定のバージョンを直接記述する以外に、バージョンと >/</>=/<=/~> 演算子を組み合わせて使用することもできます。

>= 0.2.0 でバージョン0.2.0以上。

これら演算子のうち、>/</>=/<= 辺りはどのバージョンが対象となるのか直感的に理解できると思いますが、~> は以下のようなやや特殊な挙動をします。

  • ~> 2.0.3 は、>= 2.0.3 かつ < 2.1 と同じ。

  • ~> 2.1 は、>= 2.1 かつ < 3.0 と同じ。

GitHub 以外で公開されている shard

html_builder は GitHub で公開されていましたが、shard.yml ファイルには、GitHub だけでなく、以下のような方法で shard を指定可能です。

shard の取得先
github: user/repository

GitHub リポジトリを指定する。

gitlab: user/repository

GitLab リポジトリを指定する。

bitbucket: user/repository

Bitbucket リポジトリを指定する。

git: git://git.example.org/repository.git

任意の git リポジトリを指定する。

path: ../path/to/shard

ファイルシステム上のディレクトリパスで指定する。

shard をインストールする

shard.yml で指定した shard をプロジェクト環境にインストールするコマンドは shards install です。プロジェクトディレクトリの直下でこのコマンドを実行すると、プロジェクトディレクトリの下に lib ディレクトリが作られ、その中に必要なファイル一式が展開されます。

shard のインストール
$ shards install
Fetching https://github.com/crystal-lang/html_builder.git
Installing html_builder (0.2.2)

これだけで shard のインストールは完了です。

また、インストール済みの shard は shards list コマンドで確認できます。

$ shards list
Shards installed:
  * html_builder (0.2.2)

ソースコード内で shard を使う

ひな形生成直後のソースファイル src/simple_html.cr には、プロジェクト名 simple_html をキャメルケースに変換した SimpleHtml モジュールと定義されています。この SimpleHtml モジュールはこのプロジェクトの名前空間として使用するためものです。ですので、特に理由がない限りプロジェクト固有の機能は SimpleHtml モジュールか、もしくはその中で定義した型に対して実装することになります。ただし、この時点ではまだバージョンを表す VERSION 定数が定義されているだけの状態です。

ひな形生成時のメインソースコード
# TODO: Write documentation for `SimpleHtml`
module SimpleHtml
  VERSION = "0.1.0"

  # TODO: Put your code here
end

shard として公開されている機能をプログラム中で利用するためには require 文を使用してその旨を明示する必要があります。大抵の場合 shard の README にはその shard の使用法も記載されており requrie 文の書き方もそこで確認できます。

指定された require 文と、HTML を生成する SimpleHtml.build メソッドを実装し、simple_html コマンドのソースコードが完成しました。

完成した simple_html コマンドのソースコード
require "html_builder"

# Build simple HTML source.
module SimpleHtml
  VERSION = "0.1.0"

  def self.build(args : Array(String))
    title = args[0]
    body = args[1]
    HTML.build {
      html {
        head {
          title { text title }
        }
        body {
          h1 { text title }
          p { text body }
        }
      }
    }
  end
end

if File.basename(PROGRAM_NAME) == File.basename(__FILE__, ".cr")
  puts SimpleHtml.build(ARGV)
end

実行してみる

ビルド後に試しに実行してみると html_builder の機能を使えていることが確認できました。

simple_html コマンドのコパイルと実行結果
$ crystal build src/simple_html.cr
$ ./simple_html "Page Title" "Page body."
<html><head><title>Page Title</title></head><body><h1>Page Title</h1><p>Page body.</p></body></html>

動作に問題がないようであれば、最適化フラグ --release を付けた状態で再度ビルドしておきましょう。

最適化フラグを付けたビルド
$ crystal build --release src/simple_html.cr

以上がインターネット上で公開されている shard を利用する際のざっくりとした手順になります。

Note
説明をシンプルにするため、今回の simple_html コマンドにはあえて省いている機能が多くあります。例えば、現状ではコマンドラインパラメータが足りなかったり多すぎたりする状況が全く考慮されていません。コマンドラインパラメータが足りなければ実行時例外(IndexError)を吐いて異常終了してしまいますし、多すぎた場合には3つめ以降が単に無視されてしまいます。実際に使用するコマンドラインツールを作成する場合には、コマンドラインパラメータのバリデーションやエラー処理などが必要になります。

補足: shards コマンドのもつ機能

shard のインストールに使用した shards コマンドには、インストール以外にも shard を管理するための各種機能が用意されています。

例えば、Crystal 本体のバージョンを上げたら shard が動かなくなった場合など、インストール済みの shard をアップデートしなければならない場面に遭遇することがあります。しかし、shard のインストール時に使用した shards install コマンドは、すでにインストールされている shard については処理を行いません。そのため、shards install を再度実行したとしてもインストール済みの shard はバージョンアップできません。これは、たとえ shard.yml で新しいバージョンを明示的に指定したとしても同様です。

こうした場面では、替わりに shards update コマンドを使用します。このコマンドはインストール済みの shard が shard.yml で指定したバージョンと異なっていれば再取得してくれます。また、shard.yml でバージョンが指定されていない場合は、該当の shard を最新の状態に更新します。

この他にも、インストール済みの shard を一覧表示してくれる shards list などいくつかの機能が用意されており、使用可能なコマンドやオプションは shards help で確認できます。

shard を作る

Crystal が標準で提供していない様々な機能を使えるようになる、というだけでも Shards が有用な仕組みだということはご理解いただけると思います。では、自作のライブラリを shard として公開するモチベーションとは何でしょう?

「世界中の Crystal 使いの人々に便利な機能を提供したい」という理由も当然アリです。ただ、そこまで肩肘を張らず「自分が便利だから」というだけでも、自作ライブラリを shard として公開する理由としては十分です。実際、複数のプロジェクトで共通使用する汎用部品は、仮に自分だけしか使わなさそうであっても shard として公開しておくことでデプロイ時の手間やその後のメンテナンスをかなり省力化できます。

例えば、ライブラリを開発環境とは別の本番環境で使用したい場合、プロジェクト全体を tar で固めて scp で転送、なんてことをしがちです。こうした作業はただでさえ手間がかかりますが、使用するライブラリのバージョンが異なる複数のプロジェクトを維持していこうと思うと気が遠くなります。しかし、もしそのライブラリが shard として公開されていれば、shard.yml に2〜3行追加してから shards install を実行するだけですみます。さらに、必要であれば個々のプロジェクトごとに過去のバージョンを指定してインストールすることもできます。

これだけでも、自作ライブラリを shard 化しておくメリットはあるのではないでしょうか。

「プログラミング言語の拡張ライブラリを公開する」というと敷居が高く感じられるかもしれませんが、Crystal の shard を公開する手順は比較的シンプルです。実際、crystal init コマンドで作成したひな形をベースにコードを書いて、特に何も考えずにそのまま GitHub へ公開するだけでも、とりあえず shard として使えてしまったりします。

とはいえ、ある程度は shard の体裁というものもありますので、ここからは公開リポジトリで shard を公開する際に必要な最低限の手順について説明したいと思います。

Note
今回は作成した shard を GitHub 上で公開することを想定しています。GitHub や git の操作方法についてはすでにご存知だということを前提としていますので、これらの使い方については各種 Web サイトや書籍などをご参照ください。

shard の名前

アプリケーションを作成する場合と同様、何はともあれ shard として再利用したいライブラリに持たせる機能と、その名前を決める必要があります。機能については既にやりたいことがあるはずですが、一般に公開する shard の名前は意外と悩みどころです。

shard.yml ファイルの記述方法を定義した「 shard.yml specification. 」には、shard の名前として以下のような命名規則が規定されています。

  • 他の shard と重複しないこと

  • 50 文字以下

  • 英文字は小文字(a-z)を使用すべき

  • 名前の一部に「 crystal 」を含むべきではない

  • 数字(0-9)を含んでも良いが、先頭に置いてはならない

  • アンダースコア(_)やダッシュ(-)を含んでも良いが、先頭や末尾に置いてはならない

  • アンダースコアやダッシュが連続してはならない

基本的にこれらの条件に従う必要がありますが、shard 名の重複についてはそこまで厳密に考えなくても問題になることは少ないでしょう。単機能の shard 名は割とカブりがちですし、その名前を世界中の誰も使っていないことを確認することは現実的には不可能です。とはいえ、最低でも標準添付のライブラリとカブるような名前は避ける必要があります。また、余裕があれば CrystalShards.xyz で使いたい名前を検索して、ヒットするかどうか確認くらいはしてみても良いかもしれません。

また、既存 shard の例をいくつかみてみると、shard の命名スタイルには大きく分けて2つのパターンがあるようです。

  1. html_buildermysql など、機能をそのまま表した名前。単機能とは言わないまでも何か1つの対象に焦点を絞った shard に多い。

  2. kemaltopaz など、機能とは関係なくプロダクトイメージなどからつけられた独自の名前。フレームワークなどある程度の規模を持ち、複数の機能から構成されている shard に多い。

これらを参考にわかり易い、またはカッコいい shard 名を考えてみてください。

ここでは整数 n に対して n 番目のフィボナッチ数を返してくれる shard を作ってみることにしましょう。単機能のシンプルな shard ですので、shard 名はそのまま fibonacci としました。

shard 名はスネークケースで

命名規則上はダッシュ記号も使えることになっていますが、shard 名には基本的には単語の区切りにアンダースコアを使用するスネークケースで命名することをお勧めします。何故かというと、shard 名がソースコードのファイル名にも使用される場合が多いことがその理由です。Crystal 公式ドキュメントの「コーディングスタイル」では、ソースファイルのパスを名前空間に合致させ、ファイル名として型名をスネークケースに変換したものを使用するよう推奨されています。

HTTP::WebSocket → http/web_socket.cr

Crystal公式ドキュメント「コーディングスタイル」

https://crystal-lang.org/docs/conventions/coding_style.html

ちなみに、shard 名をスネークケースで命名してプロジェクト名にも使用すれば、プロジェクトひな形で生成されたソースファイル名をそのまま使用できます。

また、同コーディングスタイルでは、型名(クラス名やモジュール名)として複数の単語を先頭文字のみ大文字にしてそのまま連結したアッパーキャメルケースを使用することが推奨されています。例えば、crystal init コマンドによるプロジェクトひな形の自動生成では、プロジェクト名をアッパーキャメルケースへ変換した名前で名前空間用のモジュールが定義されます。この点からも shard 名はスネークケースで付けておくと便利です。

プロジェクトひな形の作成

shard を作成する際も、それ用のプロジェクトを立ち上げることになります。プロジェクト名は特にこだわりがなければ shard 名と同じで構いません。というよりむしろ、ひな形で提供される各種ファイルを有効利用するには、プロジェクト名と shard 名をそろえておく方が良いでしょう。

独立したアプリケーションではなく、再利用可能なライブラリを作る場合のプロジェクトひな形生成コマンドは crystal init lib プロジェクト名 です。

fibonacci shard 用プロジェクトのひな形生成コマンド
$ crystal init lib fibonacci

なお、ライブラリを作る場合もアプリケーションを作る場合も、crystal init コマンドで生成されるファイルやディレクトリの構成に違いはありません。

shard.yml の記述確認

shard を使用する際にも使用した shard.yml ですが、本来このファイルはプロジェクトを shard として公開する際のメタデータを記述するためのものです。公開 shard にはメタデータとして最低限以下のような情報が必要です。

ひな形に含まれるメタデータ
name

shard の名前。プロジェクト名がそのまま使用される。

version

shard のバージョン情報。ひな形生成時は 0.1.0

authors

shard 作成者のリスト(配列)。git の設定で user.nameuser.mail が設定されていればそれらが使用される。

crystal

shard が対応する Crystal のバージョン。ひな形を生成した crystal コマンドのバージョンが使用される。

license

shard 公開時に採用するライセンスの種類。デフォルトでは MIT ライセンスが設定されている。

上のリストにもある通りこれらの情報はひな形生成時に自動生成されますので、もある程度そのままで利用可能な状態になっています。複数人で開発している場合に authors を追加したり、MIT 以外のライセンスを license に指定したりするなど、必要に応じてこれらの値を変更してください。また、作りたい shard に別の shard の機能が必要であれば、アプリケーションを作成した際と同様に dependencies の設定を追加します。

ひな形生成時の shard.yml
name: fibonacci
version: 0.1.0

authors:
  - Your Git Name <your_mail@example.org>

crystal: 0.27.0

license: MIT

fibonacci では他の shard を使用することもなく、特に変更すべき内容もありませんのでこのまま進めることにします。

shard.yml ファイルのフォーマット

拡張子が .yml となっていることからもわかる通り、shard.yml ファイルは YAML で書かれています。

「 shard.yml specification.」には、shard.yml 自体のルールも以下のように定義されています。shard.yml を修正した場合は、これらの条件から外れないように注意しましょう。このほか、「 shard.yml specification.」ではひな形に登場しない項目がいくつも説明されています。状況によっては便利な項目もありますので、一度詳しく目を通しておくことをお勧めします。

  • YAML ドキュメントとして構文的に正しいこと

  • 文字コードは UTF-8 を使用すること

  • 空白2文字でインデントすること

  • 文字列、配列、ハッシュ以外の YAML の要素を使用しないこと

テストケースを書く

最近ではテスト駆動開発(test-driven development)や振る舞い駆動開発(behavior driven development)が流行りのようです。テスト実行機能がコンパイラに標準で用意されていたりすることから、Crystal としてはこうした開発手法が想定されているようです。これらの方式では、まず想定されるパブリックメソッドの挙動(テストケース)を定義し、その後にテストケースを満足させるようにコードを実装する、という流れで開発を進めます。

プロジェクトひな形にはあらかじめテスト用の構成が含まれていますので、ソースファイルにコードを書き始める前に、まずは fibonacci のテストケースを書いてみましょう。

fibonacci shard の使用イメージ
require "fibonacci"

Fibonacci.number(3)  #=>  2_big_i

今回は shard 名(プロジェクト名)が fibonacci ですので、名前空間用のモジュールは Fibonacci です。提供するメソッドは n 番目のフィボナッチ数を返す Fibonacci.number(n) のみ。引数 n は整数(Int 型のいずれか)でさえあれば良いでしょう。ただし、結果として受け取るフィボナッチ数は n が大きくなると急激に大きくなります。n が200を超える頃には128ビット整数でも表現しきれないサイズになりますので、こちらは可変長整数(BigInt)型を使用することにします。また、あまり一般的ではありませんが、フィボナッチ数は n が負の場合も定義されています。せっかくですので、n が負のフィボナッチ数にも対応させることにしましょう。

プロジェクトひな形にはテストケースを定義するための spec/fibonacci_spec.cr が含まれています。ただし、ここで定義されている内容は「 falestrue と等しいはず」という内容で、当然ながらこの状態でテストを実行しても必ず不合格になります。

ひな形生成時の fibonacci_spec.cr
require "./spec_helper"

describe Fibonacci do
  # TODO: Write tests

  it "works" do
    false.should eq(true)
  end
end

fibonacci の挙動に合わせて必要なテストケースを定義すると以下のようになりました。

テストケースを定義した fibonacci_spec.cr
require "./spec_helper"

describe Fibonacci do
  describe ".number(n : Int)" do
    it "returns fibonacci numbers correctly." do
      Fibonacci.number(5).should eq BigInt.new(5)
      Fibonacci.number(10).should eq BigInt.new(55)
    end

    it "returns negative fibonacci numbers correctly." do
      Fibonacci.number(-5).should eq BigInt.new(5)
      Fibonacci.number(-10).should eq BigInt.new(-55)
    end

    it "returns very large fibonacci number correctly." do
      Fibonacci.number(200).should eq BigInt.new("280571172992510140037611932413038677189525")
      Fibonacci.number(-200).should eq BigInt.new("-280571172992510140037611932413038677189525")
    end
  end
end

テストケースの書き方はこの章の最後で詳しく紹介しますので、ひとまずは「そういうもの」として先へ進んでください。

機能の実装

必要なテストケースが準備できたら、次はそのテストケースを満足させる機能を実装していきます。テストはパブリックなインタフェース(メソッド)の入出力を定義しているだけですので、メソッドがどう実装されるかは気にしません。

fibonacci の実装方法も何パターンか考えられますが、とりあえず、一度計算した内容のキャッシュくらいは持てるように実装してみたのが以下のコードです。

fibonacci.cr のソースコード
require "big"

# Retuen fibonacci number.
module Fibonacci
  VERSION = "0.1.0"

  @@numbers = [BigInt.new(0), BigInt.new(1)]

  def self.number(n : Int)
    negative = (n < 0)
    even = n.even?
    n = n.abs
    while @@numbers.size < n + 1
      @@numbers << @@numbers[-1] + @@numbers[-2]
    end
    if negative && even
      -@@numbers[n]
    else
      @@numbers[n]
    end
  end
end

テストの実行

テストの実行コマンドは crystal spec です。プロジェクトディレクトリの直下でこのコマンドを実行すると用意したテストケースが順に評価され、実際の挙動が指定された内容通りかどうかチェックされます。

テスト実行結果
$ crystal spec
...

Finished in 133 microseconds
3 examples, 0 failures, 0 errors, 0 pending

テスト結果の先頭行には、テストケース(it ブロック)ごとの実行結果がそれぞれ1文字で表示されます。表示されたのが . であれば、そのテストケースには合格できたことを表しています。もしここに . 以外の文字が表示されているようであれば、対応した it ブロック内でテストした機能の実装に問題があることになります。先頭行がすべて . になるまで、ソースコードの修正とテストを繰り返しましょう。

今回の spec ファイルには it ブロックが3つあり、上の例では . が3つ並んでいます。ということは、実装したソースコードは当初想定した通りの挙動をしているようです。

このように、テストが成功した時点で shard の機能面は完成です。実際、この状態でも GitHub へ公開すれば、shard として使用できてしまったりします。しかし、曲がりなりにも一般公開する shard には、それなりの体裁といったものもありますので、もうひと手間ふた手間かけてみることにしましょう。

README を書く

ひな形生成時に用意されている README.md ファイルは、以下の内容をカバーするように構成されています。

README.md ひな形の構成
# shard 名

README のタイトルに当たるパート。shard の概要を記載する。

## Instration

shard をインストールするための手順を記載するパート。shard.yml へ追加する dependencies の内容は用意されている。shard 以外の外部ライブラリやコマンドに依存している場合はここで説明しておいた方が良い。

## Usage

ソースファイル内での shard の使い方を記載するパート。shard の require 方法は用意されている。最低限のサンプルコードくらいは書いておくべき。

## Development

開発者向け情報を記載するパート。shard の開発に参加したり、自身で改造したりしたい人向けの情報を記載する。積極的に開発者を募るつもりがなければ項目ごとオミットしても良いかも。

## Contributing

shard の開発に参加してくれる人向けの参加手順を記載するパート。積極的に開発者を募るつもりがなければ項目ごとオミットしても良いかも。

## Contributors

shard の作成に携わった人のリストを記載するパート。

また、このひな形は GitHub での公開を前提としています。そのため、GitHub で shard を 公開する場合には、以下の2点を修正するだけで標準的な README として利用できるようになっています。

  1. 何箇所かある [your-github-name] を自分の GitHub アカウントに置き換える

  2. TODO: の部分を埋めていく

なお、ひな形の中で登場する TODO: は以下の3箇所です。

README.md 中に登場する TODO: 項目
TODO: Write a description here

その shard の概要。最低限、なにをする shard なのかくらいは書いておいた方が良い。

TODO: Write usage instructions here

その shard の使い方。ある程度サンプルコードで代用可能。

TODO: Write development instructions here

その shard の開発に関わろうとする人向けの情報。

fibonacci はメソッドが1つだけしかなく機能もシンプルなので、Usage は最低限のサンプルコードで済ませることにします。また、共同開発者を積極的に募るほどのモノでもありませんので、最低限の項目を記載した README.md は以下のようになりました。

完成した README.md
# fibonacci

The fibonacci number calculator for the Crystal programing language.

## Installation

1. Add the dependency to your `shard.yml`:
```yaml
dependencies:
  fibonacci:
    github: github_name/fibonacci
```
2. Run `shards install`

## Usage

```crystal
require "fibonacci"

f0 = Fibonacci.number(0) #=> 0_big_i
f1 = Fibonacci.number(1) #=> 1_big_i
f2 = Fibonacci.number(2) #=> 1_big_i
f3 = Fibonacci.number(3) #=> 2_big_i
f4 = Fibonacci.number(4) #=> 3_big_i
```

## Contributors

- [github_name](https://github.com/github_name) Your Git Name - creator, maintainer

ライセンスを選ぶ

プロジェクトのひな形では、shard を公開する際のライセンスとして、MIT ライセンスが選択されています。

shard.ymllicense には MIT が指定されており、プロジェクトディレクトリ内の LICENSE ファイルも MIT ライセンスの条項が記載されています。MIT ライセンスとは、ざっくりいうと「著作権表記とライセンス条項さえ含まれていれば有償無償問わず自由に使って良い、ただし無保証」というものです。ソフトウェアの利用者としてはかなり自由に利用でき、開発者としては免責が明言されているため、比較的使いやすいライセンスの1つです。大した量ではありませんので、詳細については一度 LICENSE ファイルの内容を読んでみてください。

もし MIT 以外のライセンスを採用したければ、LICENSE ファイルの内容を使用したいライセンス条件に書き換えて shard.ymllicense を変更することになります。このとき、shard.ymllicense には OSI(Open Source Initiative)で定義されたライセンス名か、ライセンスの参照先 URL を指定可能です。

今回は特にライセンスへのこだわりもありませんので、MIT ライセンスのままで行こうと思います。

GitHub へ公開する

ここまでの準備が完了したら、プロジェクトを GitHub へ公開しましょう。この辺りの手順は GitHub のドキュメントなどを参照してください。

プロジェクトが GitHub へ公開されると、別のプロジェクトが shard.yml に以下の記述を追加することで fibonacci をインストール可能になります。

dependencies:
  fibonacci:
    github: github_name/fibonacci

ここまでで shard を作って公開するまでの手順は完了です。

ただし、この状態では shards コマンドが shard のバージョンを認識できません。fibonacci を使おうとするプロジェクトで shard.yml に特定のバージョンを指定していても、常に最新状態(リポジトリの HEAD)の shard がインストールされてしまいます。

shard を作る手順の最後に、shard のバージョン管理方法をご紹介しましょう。

shard のバージョン管理

毎度おなじみ「 shard.yml specification.」には、shard のバージョンとして以下のようなルールが示されています。

  • セマンティックバージョニングに従うことが望ましい

  • 数字が含まれていること

  • .- を使用しても良いが、連続しないこと

    0.0.11.2.32.0.0-rc1 など

強制はされないものの、セマンティックバージョニングのような合理的なバージョン付けを強く推奨。

後方互換が損なわれる大きな変更でも、内部の子細なバグフィックスでもメジャーバージョンが上がるようでは、使う側としてバージョンの変化からバージョンアップのインパクトを測りかねてしまいます。

セマンティックバージョニングではメジャー/マイナ/パッチという3つの数字でバージョンを構成し、パッチよりマイナ、マイナよりメジャーバージョンが上がることのインパクトが大きくなるよう定義されています。shard.yml 内でバージョンを指定する際の ~> 演算子は、セマンティックバージョニングのような、先頭に近い数字の変更がインパクトが大きい際に有効に働きます。

セマンティックバージョニング

https://semver.org/lang/ja/

3つのバージョン情報

fibonacci プロジェクトでは、プロジェクトファイルの2箇所に shard のバージョンが登場します。1つ目は shard.yml ファイル2行目の version 、もう1つがソースファイルで定義されている Fibonacci::VERSION 定数です。この2つの値は、ひな形生成時にはどちらも 0.1.0 となっており、これがプロジェクト(shard)のバージョンになります。

前述の通り、shard.yml とソースファイルにバージョンが書かれていても、shards コマンドはそのバージョンを認識できません。

このとき使用されるのが第三のバージョン情報が、git リポジトリのバージョンタグです。

shard.ymldependencies 内で、ある shard のバージョンとして 1.0.0 が指定されていたとしましょう。このとき、shards コマンドは、その shard のリポジトリから v1.0.0 とタグ付けされたコミットの状態をインストールしようとします。

つまり、先ほどプッシュした fibonacci の GitHub リポジトリに v0.1.0 というタグを付けることで、現状の fibonacci がバージョン 0.1.0 だと明示できます。なお、GitHub ではリリース管理機能を使って新しいリリースを追加する際に、Web UI からバージョンタグを設定できて便利です。

もし fibonacci の機能に修正を加えてバージョンを 0.2.0 へ上げる場合には以下のような手順で行うことになるでしょう。こうしておくと、複数のプロジェクトからそれぞれ異なるバージョンの fibonacci を利用可能になります。

  1. fibonacci の機能を修正

  2. ソースファイル上の Fibonacci::VERSION 定数を 0.2.0 に変更

  3. shard.yml 2行目の version0.2.0 に変更

  4. 変更をコミットし、GitHub 上の master ブランチへマージ

  5. GitHub 上で変更がマージされた master ブランチにバージョンタグ v0.2.0 を追加

というわけで、以上が shard を作って公開し、さらに他のプロジェクトから使用してもらうために必要な作業の流れになります。

是非みなさんも自作のライブラリを shard として公開してみてください。世界中には自分と同じことで困っていて、自分だけしか使わないだろうと思っていた機能を便利に使ってくれる人が意外といたりしますよ。

テスト

この章の最後に、shard を作る際にも使用したテストについて簡単にご紹介しましょう。

ここでいうテストはソフトウェアに対するユニットテストの一種で、プログラムが想定した通りの挙動を取るかどうかを調べる機能試験に相当します。これは、あらかじめ「このメソッドにこういった引数を与えると、こんな結果になるはず」というプログラムの挙動(テストケース)を列挙しておき、実際にそうなるかどうかを個々に評価するような仕組みです。

Crystal には、RSpec を参考にしたテスト機能を提供するモジュール Spec がコンパイラ自身の標準添付ライブラリとして用意されています。また、標準のプロジェクトひな形にテスト用のファイル一式が含まれているなど、言語自体がテストを強く意識した作りになっています。

テストの使い方

細かい説明は後にして、まずはテストの使い方をざっくり見てみましょう。

spec ファイルを作る

まず、テストにはテストケースを定義した spec ファイルが必要です。spec ファイルは、"spec" とテスト対象となるコードを require した Crystal のソースファイルで、テストケース自体も Crystal のコードとして定義します。

さて、ここでは簡単な例として "がおー" と吠える Bear クラスを実装する場合を考えてみましょう。Bear クラスが持つインスタンスメソッドは吠え声を文字列で返す #bark のみ、ソースファイル名は bear.cr です。spec ファイルの名前はテスト対象とするファイルのベースネームに続けて _spec.cr を加えたものが使用されることが一般的です。今回もその例に倣い、熊が正しく "がおー" と吠えるかどうかテストする bear_spec.cr を、ソースファイルと同じディレクトリに作ってみました。

bear_spec.cr
require "spec"
require "./bear"

describe Bear do
  describe "#bark" do
    it "returns \"がおー\"." do
      Bear.new.bark.should eq "がおー"
    end
  end
end

最初の2行は Spec モジュールとソースコードの require 文で、4行目以降がテストケースの定義部分になります。

まず登場するのが describe Bear で宣言されたブロックです。これは、その内部で行われるテストが Bear 型を対象としていることを明示しています。describe Bear ブロックの中には、もう1つ describe "#bart" ブロックが置かれています。こちらのブロックはその内部で Bear 型の中でも #bart メソッドに関するテストを行うことを宣言しています。

続いて登場するのがテストケースに相当する it ブロックです。it ブロックでは、describe で宣言された対象となるメソッドの、ある1つの振る舞いに関するテストを行います。また、it ブロックには引数としてその内部で行うテスト内容の説明文を与えることができます。この例では、itBear#bart メソッド)が、正しく "がおー" という文字列を返すかどうかについてテストすることが説明されています。

そして、it ブロックの内で実行される Bear.new.bark.should eq "がおー" が実際に評価されるテスト内容の記述です。Bear.new.bark"がおー" と等しく(eq)あるべき(should)といったように、実際に評価している内容が(英語として)比較的自然に読める構文になっています。

プログラム本体を実装する

さて、spec ファイルが用意できたので次に Bear クラスそのものを bear.cr へ実装していくのですが、ついうっかり熊に "わん" と吠えさせてしまいました。

実装ミスを含んだ bear.cr
class Bear
  def bark
    "わん"
  end
end

この状態でテストを実行してみるとどうなるでしょうか。なお、テストの実行コマンドは crystal spec です。特定の spec ファイルを対象としたい場合には、その spec ファイル名をパラメータとして与えてください。

テストの実行結果
$ crystal spec bear_spec.cr
F

Failures:

  1) Bear #bark returns "がおー".
     Failure/Error: Bear.new.bark.should eq "がおー"

       Expected: "がおー"
            got: "わん"

     # bear_spec.cr:7

Finished in 112 microseconds
1 examples, 1 failures, 0 errors, 0 pending

Failed examples:

crystal spec bear_spec.cr:6 # Bear #bark returns "がおー".

テスト結果の先頭行には、テストケース(it ブロック)ごとの実行結果サマリが、それぞれ1文字で出力されます。今回はテストケースが1つしかありませんので、表示されているのは F が1文字だけです。テスト結果 F は、実際の挙動がテストケースで指定された条件とは異なる結果を示したため、テストが不合格になった場合に表示されるものです。

このように何らかの理由でテストが不合格になると、Failures: に続いて具体的な問題点が出力されます。ここでは、期待された値(Expected:)が "がおー" なのに対して、実際の結果(got:)が "わん" になっていたことが確認できます。このように、テストを行うと、実装されたコードが想定外の挙動を示した際に、どの部分がどのように異なっていたのかを具体的に知ることができます。

テスト結果を元にソースを修正する

テスト結果から問題の箇所は明らかですので、bear.cr の実装を修正しました。

修正後の bear.cr
class Bear
  def bark
    "がおー"
  end
end

もう一度テストを実行してみると、今度は F ではなく . が表示されましたので、実装したコードが当初想定した通りの機能を実現できている様子が確認できました。

テストの実行結果(ソース修正後)
$ crystal spec bear_spec.cr
.

Finished in 46 microseconds
1 examples, 0 failures, 0 errors, 0 pending

このように、まず実装しようとする機能の挙動をテストケースとして定義し、テストケースを満足するように実装を進める開発手法を、テスト駆動開発や振る舞い駆動開発などと呼びます。この手法のメリットとしては、実装コードの品質が担保されるという点はもちろんありますが、テストケース自体がある意味で機能仕様やサンプルコードとして利用できるという点も見逃せません。チームで開発している場合や他の人が書いたコードを引き継ぐ場合などでも、適切なテストケースが用意されていれば最低限の情報はそこから入手可能です。

テスト関連の構文

では次に、spec ファイルで使用する各構文についてみてみましょう。

グルーピング用ブロック

グルーピング用ブロックは、1つ以上のテストケースを何らかの基準でまとめるために使用されます。グルーピング用ブロックの内側には次に紹介するテストケース用のブロックを置くだけでなく、別のグルーピング用ブロックをネストさせることも可能です。

先の例で登場した describe もグルーピング用ブロックの1種です。describe ブロックはテスト対象を元にしたグルーピングを行うもので、テスト対象として型(クラス、モジュールなど)やメソッド名(文字列で指定)を引数として与えることができます。この引数はテスト不合格時の出力メッセージでも利用されるため、テスト対象となるメソッド名に準じた構成をとることが強く推奨されています。例えば、外側の describe では型を指定し、内側の describe にメソッド名を渡すような形です。またこのときのメソッド名に、クラスメソッドであれば . を、インスタンスメソッドであれば # を先頭に付けておくと、Class #method のように準的なメソッド表記に近い形を再現できます。

Crystal の spec ファイルでは、グルーピング用のブロックとして describe 以外にもう1つ context が使用可能です。describe ブロックがテスト対象をもとにしたグルーピングだとすると、context ブロックはその時の状況(条件)によるグルーピングです。例えば、配列が要素を含んでいるか否かで挙動が異なる場合に、それぞれの状況を明示できます。

describecontext
require "spec"

describe Array do
  describe "#first?" do
    context "self is empty." do
      it "returns nil." do
        Array(Int32).new.first?.should be_nil
      end
    end

    context "self has elements." do
      it "returns receiver's first element." do
        [1, 2, 3].first?.should eq 1
      end
    end
  end
end

テストケース用ブロック

こちらの例としては it がすでに登場しています。it はあるメソッドがもつ特定の挙動1つについてのテストケースを定義するためのブロックです。ですので、複数の挙動に対するテスト内容を1つの it ブロック内に記述すべきではありません。例えば、オブジェクトの状態にや引数の値によって異なる挙動を示すような場合は、同じメソッドであってもそれぞれに独立した it ブロックを設けてテストケースを定義すべきです。

テストケースを書くためのブロックには it ともう1つ、pending があります。pending ブロックは文字通り、一時的にそのテストケースの実行を保留する場合に使用します。例えば、機能仕様としてのテストケースは用意されたものの対象となるメソッドがまだ実装されていないような場合に、it の代わりに pending ブロックを使用すると良いでしょう。ただし、あくまで 一時的に保留しておく だけですので、最終的には pending ブロックを it ブロックに置き換えてテストに合格できるようにしなければいけません。

itpending
require "spec"
require "./bear"

describe Bear do
  describe "#bark" do
    it "returns \"がおー\"." do
      Bear.new.bark.should eq "がおー"
    end
  end

  describe "#bite" do
    # Bear#bite は未実装
    pending "retuns \"がぶがぶ\"." do
      Bear.new.bite.should eq "がぶがぶ"
    end
  end
end

オブジェクトの状態を確認する

require "spec" が実行されると、全てのオブジェクトに対して #should#should_not という2つのインスタンスメソッドが追加されます。#should は、レシーバの状態に対する評価条件(eq "がおー" など)を引数に取り、実際の値がその条件を満たしていないとテストが不合格になります。一方、#should_not は逆に評価条件を満たしてしまうとテストが不合格になります。

オブジェクトの状態に対する評価条件には以下のような種類があります。一部の評価条件は特定のインスタンスメソッドを持たないオブジェクトに対しては使用できません。例えば、should be を使用するには #same? が実装されている必要があります。

オブジェクトの状態を評価する条件一覧
actual.should eq expected

actualexpected と等しいかどうか。
条件: actual == expected

actual.should be expected

actualexpected と同値とみなせるかどうか。
条件: actual.same?(expected)

actual.should be_a expected

actualexpected 型の値かどうか。
条件: actual.is_a?(expected)

actual.should be_nil

actualnil かどうか。
条件: actual.nil?

actual.should be_true

acrualtrue かどうか。
条件: actual == true

actual.should be_false

acrualfalse かどうか。
条件: actual == false

actual.should be_truthy

acrualif 文で真とみなされるかどうか。
条件: acrualfalsenilPointer.null のいずれでもない

actual.should be_falsey

acrualif 文で偽とみなされるかどうか。
条件: acrualfalsenilPointer.null のいずれか

actual.should be < expected

actualexpected より小さいかどうか。
条件: actual < expected

actual.should be ⇐ expected

actualexpected 以下かどうか。
条件: actual ⇐ expected

actual.should be > expected

actualexpected より大きいかどうか。
条件: actual > expected

actual.should be >= expected

actualexpected 以上かどうか。
条件: actual >= expected

actual.should be_close(expected, delta)

actualexpexted の差が delta 以下かどうか。
条件: (actual - expected).abs ⇐ delta

actual.should contain expected

actualexpexted を含むかどうか。
条件: actual.includes?(expected)

actual.should match expected

actualexpexted にマッチするかどうか。
条件: actual =~ expected

例外の発生を確認する

テストケース内で expext_raises ブロックに引数として例外の型を指定してすると、ブロック内で指定した型の例外が発生することを確認できます。

例外チェック
      it "raises an Enumerable::EmptyError." do
        expect_raises(Enumerable::EmptyError) {
          # Enumerable::EmptyError が発生しなければテスト失敗
          Array(Int32).new.first
        }
      end

このとき、引数に文字列もしくは正規表現オブジェクトを追加することで、エラーメッセージに対する条件を指定することも可能です。

メッセージ条件付き例外チェック
expect_raises(SomeError, "message") { …​ }

エラーメッセージに "message" が含まれる SomeError 型の例外が発生しなければ不合格。

expect_raises(SomeError, /pattern/) { …​ }

エラーメッセージが /pattern/ にマッチする SomeError 型の例外が発生しなければ不合格。

なお、発生した例外の状態を他の評価条件でテストしたい場合、expect_raises の返り値として例外インスタンスを取得できます。

テスト実行結果の見方

テストの実行結果は大きく分けると以下のパートから構成されます。

  1. テストケースの実行結果サマリ

  2. 不合格となったテストケースの詳細情報

  3. テストの実行に要した時間

  4. テスト結果の統計情報

  5. 不合格となったテストケースの一覧

このうち、2. および 5. については、全てのテストケースを問題なく通過できた場合には表示されません。

ソース修正前の bear_spec.cr の実行結果を例に、それぞれの内容を詳しくみてみましょう。

失敗したテストの実行結果(再掲)
$ crystal spec bear_spec.cr
F

Failures:

  1) Bear #bark returns "がおー".
     Failure/Error: Bear.new.bark.should eq "がおー"

       Expected: "がおー"
            got: "わん"

     # bear_spec.cr:7

Finished in 112 microseconds
1 examples, 1 failures, 0 errors, 0 pending

Failed examples:

crystal spec bear_spec.cr:6 # Bear #bark returns "がおー".

テストケースの実行結果サマリ(1行目)

テストケースの実行結果には合格(.)、不合格(F)、エラー(E)、ペンディング(*)の4種類があります。テストの実行結果では、先頭行にテストケースの数だけ実行結果に対応した文字が並んで表示されます。もしテストを実行したのがカラー表示対応のターミナルであれば . は緑、FE が赤、* が黄色で表示されているかもしれません。

ここで表示される実行結果が全て . になっていれば、テスト全体に合格できたことになります。

実行結果の種類
合格(.

テストケース内の評価条件を全てパスし、想定外の例外やエラーも発生しなかった場合。

不合格(F

テストケース内の評価条件を満たさない項目が存在したり、想定される例外が発生しなかった場合。

エラー(E

テストケース内で expect_raises で補足されない想定外の例外が発生した場合

ペンディング(*

テストケースが pending ブロックとして定義されており、テストの実行を保留している場合。

不合格となったテストケースの詳細情報(3〜11行目)

Failures: に続いて、不合格となったテストケースの具体的な問題点が出力されます。

不合格テストケースの情報
  1. 不合格テストケースの通し番号とテストケースの説明(5行目)

  2. 不合格の原因となったチェック項目(6行目)

  3. 想定された状態(Expected:)と実際の状態(got:)(8〜9行目)

  4. 該当チェック項目があるファイル名と行番号(11行目)

特に 3. で表示される想定値と実際の値との比較は、問題解決の大きな助けになってくれることでしょう。

テストの実行に要した時間(13行目)

テスト全体にかかった時間ですので、あくまで参考までに。

テスト結果の統計情報(14行目)

ここには examplesfailureserrorspending と4種類の数値が出力されます。

examples が spec ファイルに定義されたテストケースの数、後ろ3つは、実行結果が不合格/エラー/ペンディングとなったテストケースの数です。

ここで examples 以外の値がゼロになっていれば、テスト全体に合格したことになります。

不合格となったテストケースの再テスト用コマンド一覧(16〜18行目)

最後に、不合格となったテストケースについて、個別に再テストする場合に使える crystal spec コマンドが一覧で表示されます。

リスト各行の # から後ろは、どのテストケースについてのものなのかを識別するためのコメントです。

# よりも前の部分、この例でいう crystal spec bear_spec.cr:6 を実行することで、問題のあるテストケースだけを対象としてテストを実行できます。

crystal spec コマンドのパラメータ

テストを実行する crystal spec コマンドには、spec ファイル名以外のパラメータを指定できます。適切なパラメータを与えることで、ディレクトリ単位や spec ファイル単位、さらには特定のテストケース単位でのテストを実行可能です。

crystal spec コマンドへのパラメータ指定
crystal spec

パラメータを省略した場合、spec/**/*_spec.cr にマッチする全ての spec ファイルを対象とする。

crystal spec dir/

ディレクトリを指定した場合。dir/**/*_spec.cr マッチする全ての spec ファイルを対象とする。

crystal spec spec_file.cr

spec ファイルを指定した場合。spec_file.cr 単体を対象とする。

crystal spec file.cr:10

spec ファイルと行数を指定した場合。spec_file.cr ファイルの10行目から始まるブロックだけを対象とする。

大規模なソースコードのテストを実施する際には、これらのパラメータを有効活用することでテスト効率を向上できるかもしれません。

なお、crystal init コマンドで生成されるプロジェクトひな形ではテスト関連のファイルは spec ディレクトリに収められています。そのため、spec ファイルを指定しなくても crystal spec とだけ打てばテストが実行可能になっています。

spec ヘルパファイル

プロジェクトひな形の spec ディレクトリには、spec ファイル以外に spec ヘルパファイル spec_helper.cr が用意されています。デフォルトでは、"spec" とプロジェクトのソースファイルを require するだけの内容になっており、spec ファイルから読み込まれています。

spec ヘルパは、spec ファイルが1つだけの場合それほどありがたみを感じないかもしれません。しかし、プロジェクトが大規模になり、いくつもの spec ファイルを駆使するようになると spec ヘルパファイルが効果を発揮するようになります。例えば、テスト用のオブジェクト生成やテスト用の特別な環境設定など、複数の spec ファイルで共通的に使用するメソッドを spec ヘルパファイルに書いておけばコードの重複を防ぐことが可能です。

テストはコードのクォリティを確保する上で重要な工程ですが、慣れないうちは面倒臭く感じることも多いと思います。spec ヘルパのような仕組みをうまく活用して、効率よくテストする方法を模索してみてください。

まとめ

shard を利用する手順
  1. shard を利用するプロジェクトを作成する

  2. shard.yml に使用したい shard の記述を追加する

  3. shard をプロジェクトディレクトリにインストールする

  4. ソースコードの先頭で shard を require する

  5. ソースコード内で shard が提供する機能を使用する

shard を作る手順
  1. shard 用のプロジェクトを作成する

  2. shard.yml にメタデータを記述する

  3. テストのテストケースを書く

  4. テストケースを満足させるようにソースファイルに機能を実装する

  5. テストを実行、問題がなくなるまでソースの修正とテストを繰り返す

  6. README を書く

  7. ライセンスを選ぶ

  8. GiuHub などのリポジトリへ公開する

  9. git リポジトリにバージョンタグを付ける

テストの構文
describe ブロック

テスト対象でテストケースをグルーピングする。

context ブロック

条件や状況でテストケースをグルーピングする。

it ブロック

ある振る舞いに対するテストケースを記述する。

pending ブロック

一時的にテストを保留するテストケースに使用する。

Object#should メソッド

レシーバがある条件を満たさなければテストに不合格とする。

Object#should_not メソッド

レシーバがある条件を満たした場合にテストに不合格とする。

expect_raises ブロック

ブロック内で指定された例外が発生しなかったらテストに不合格とする。