NeuronCheckは、下記の2つの機能を、宣言的な文法で実現するRubyライブラリ (gem) です。

  1. メソッドの引数や戻り値のチェック
  2. メソッド実行前後の簡単な条件チェック(事前条件、事後条件)

簡単な記述でさまざまな種類のチェックが行えることと、プロダクション環境のパフォーマンスにほとんど悪影響を与えずに導入できることの2つが、大きな特徴です。
また、他のライブラリに組み込む形で、チェック機能部分のみを利用することもできます。

NeuronCheckは、EiffelやD言語の契約プログラミングの考え方、およびRubype gemにインスパイアされて作られており、「実行可能なドキュメントの形で、型情報や事前条件などを記述し、その記述に応じたチェックを行えるようにする」ことを目指しています。


下記のコード例は、具体的にNeuronCheckで何ができるのかを理解するための一助となるでしょう。

require 'neuroncheck'

class Converter
  # Converterクラス内でNeuronCheckを使用可能にする
  extend NeuronCheck

  # NeuronCheckのチェック宣言部
  ndecl {
    # メソッドに3つの引数があり、順に
    # 「文字列」「eachメソッドを持つ任意のオブジェクト」「数値 or nil」であることをチェック
    args String, respondable(:each), [Numeric, nil]

    # selfを返すメソッドであることをチェック
    returns :self

    # 事前条件のチェック。メソッドの呼び出し時に、引数 threshold が0以上 (非負数) でなければエラーとする
    precond do
      assert{ threshold >= 0 }
    end
  }

  # 実際のメソッド定義。呼び出し時に上で宣言したチェックが行われる
  def convert(text, keywords, threshold = nil)
    # (メイン処理)

    return self
  end
end

conv = Converter.new
conv.convert('text', ['Blog', 'Learning'], 0.5)
#=> エラーにならず、正常に実行できる

conv.convert(100, ['Blog', 'Learning'], 0.5)

# => 下記の例外が発生する
#    script.rb:34:in `<main>': 1st argument `text' of `Converter#convert' must be String, but was 100 (NeuronCheckError)
#             got: 100
#       signature: Converter#convert(text:String, keywords:respondable(:each), threshold:[Numeric, nil]) -> self
#     declared at: script.rb:11:in `block in <class:Converter>'

ndeclで引数や戻り値の型、事前条件を宣言しておくことで、次に定義するconvertメソッドに対するチェックを実行することができます。

また、引数/戻り値の型チェックだけが必要な場合は、Ruby 2.1以降で正式導入されたRefinement機能を併用することで、次のように短縮して書くこともできます。

require 'neuroncheck'
using NeuronCheckSyntax # このファイル内のみでNeuronCheckによるチェック宣言を使用可能にする

class Converter
  # 1つ前の例のndeclと同じ動作。引数3つと戻り値を同時にチェックする
  sig String, respondable(:each), [Numeric, nil] => self

  def convert(text, keywords, threshold = nil)
    # (メイン処理)
  end
end

実際の使い方についてもっと詳しく知りたい場合は、基本的な使い方を参照してください。

特徴

NeuronCheckは下記のことを目指して作られています。

できる限り自然な記述でチェックが行えること

Rubyは「ストレスなく書けること」を重視する言語ですが、特に型チェックや基本的な条件チェックのような記述については、できるかぎり簡潔に、直感的に書けることが重要になります。
そうでないとチェックすることそのものがストレスとなり、チェックをしようという意欲を削いでしまうからです。

NeuronCheckはできる限り短く、宣言的な記述で、チェックを行えるようにすることを心がけています。

パフォーマンスに影響を与えないこと

チェック処理は通常、パフォーマンスに影響を与えるような処理ではありません。
とくに型チェックや基本的な範囲チェックのような、簡単なものであればなおさらそうです。
それは処理時間の上ではほぼ無視していいような時間しかかからないのです。

……しかし、そう頭で理解してはいても、やはりパフォーマンスは気になってしまうものです。
私たちプログラマは、チェック処理を書くたびに「このチェック処理はどのくらいパフォーマンスに影響があるだろうか」ということを意識したくはありません。
それは気にしなくてはならないことを増やし、私たちのストレスになるからです。

この問題についてNeuronCheckが選択したアプローチは、EiffelやD言語と同じものです。
つまり、プロダクション環境(リリース環境)では全てのチェックをオフにすることができる ということです。
これにより、プロダクション環境での性能がチェック処理によってほとんど悪化しないことが保証され、プログラマは少しばかり時間のかかるチェック処理でも、気にせずに実行することができるようになります。

下記はSinatraでWebアプリケーションを作成した際に、production環境でのみ全てのチェックを無効化する例です。

# production環境のときは、NeuronCheckの全てのチェック処理をスキップさせる
configure :production
  NeuronCheck.disable
end
その他
  • インスパイア元であるRubypeと同じく、漸進的なチェックの導入が可能です。必要なメソッドにだけ、部分的にチェックを追加することができます。

  • 簡単なプラグイン機構を備えており、ユーザーが独自のチェック用キーワードを追加することができます。追加したキーワードは、引数や戻り値のチェックで使用することが可能です。
    詳細は独自キーワードの追加を参照してください。

  • NeuronCheck.get_declarations_as_json メソッドにより、宣言した全メソッドのチェック情報を一括で取得することができます。取得した情報はJSON化しやすい形になっており、他のツールからの利用が可能です。
    詳細は宣言情報の取得APIを参照してください。