module NeuronCheckSystem::Kernel

メソッド追加時のフック処理や、属性宣言メソッドのオーバーライドなどを定義したメインモジュール NeuronCheckを行いたい対象のモジュールやクラスにextendすることで使用する

Public Instance Methods

__neuron_check_attr_defined(used_method_name, names, use_reader: false, use_writer: false) click to toggle source
Calls superclass method
# File lib/neuroncheck/kernel.rb, line 65
def __neuron_check_attr_defined(used_method_name, names, use_reader: false, use_writer: false)
  # 直前にNeuronCheck宣言部があれば、その宣言内容を各メソッドへ登録する
  if (declaration = @__neuron_check_last_declaration) then
    # 短縮記法による宣言かどうかで分岐
    if declaration.shorthand then
      # 短縮記法の場合は、引数が2つ以上宣言されている、もしくは戻り値が宣言されている場合にエラーとする
      if declaration.arg_matchers.size >= 2 or declaration.return_matcher then
        raise NeuronCheckSystem::DeclarationError, "expected value must be one for `#{used_method_name}'", declaration.declared_caller_locations
      end

      # 引数1用のマッチャを属性用のマッチャとみなす
      target_attr_matcher = declaration.arg_matchers[0]
    else
      # 通常の宣言の場合、引数、戻り値の宣言がされている場合はエラーとする
      if declaration.arg_matchers.size >= 1 or declaration.return_matcher then
        raise NeuronCheckSystem::DeclarationError, "`args' or `returns' declaration can be used only for method definition, but used for `#{used_method_name}'", declaration.declared_caller_locations
      end

      target_attr_matcher = declaration.attr_matcher
    end

    # 属性チェック用モジュールに対する処理
    @__neuron_check_attr_check_module.module_eval do

      # 属性1つごとに処理
      names.each do |attr_name|
        # 属性チェック用モジュールに、readerチェック用のラッパーメソッドを追加する
        if use_reader then
          define_method(attr_name) do
            # 通常の処理を呼び出す
            val = super()

            # 属性宣言があればチェック処理
            if target_attr_matcher then
              unless target_attr_matcher.match?(val, self) then
                context_caption = "value of attribute `#{self.class.name}##{attr_name}'"
                raise NeuronCheckError, target_attr_matcher.get_error_message(declaration, context_caption, val), (NeuronCheck.debug? ? caller : caller(1))
              end
            end
          end
        end

        # 属性チェック用モジュールに、writerチェック用のラッパーメソッドを追加する
        if use_writer then
          define_method("#{attr_name}=") do |val|
            # 属性宣言があればチェック処理
            if target_attr_matcher then
              unless target_attr_matcher.match?(val, self) then
                context_caption = "value of attribute `#{self.class.name}##{attr_name}'"
                raise NeuronCheckError, target_attr_matcher.get_error_message(declaration, context_caption, val, phrase_after_but: 'set'), (NeuronCheck.debug? ? caller : caller(1))
              end
            end

            # 通常の処理を呼び出す
            super(val)
          end
        end

      end
    end

    # 属性1つごとに処理
    names.each do |attr_name|
      # 登録実行
      NeuronCheckSystem::ATTR_DECLARATIONS[self][attr_name] = declaration
      declaration.assigned_class_or_module = self
      declaration.assigned_attribute_name = attr_name
    end

    # チェック処理の追加が完了したら、「最後に宣言した内容」を表すクラスインスタンス変数をnilに戻す
    @__neuron_check_last_declaration = nil
  end
end
__neuron_check_method_added_hook(target_cls_or_mod, method_name, met, singleton_original_class = nil) click to toggle source

メソッド/特異メソッドを定義したときの共通処理

# File lib/neuroncheck/kernel.rb, line 159
def __neuron_check_method_added_hook(target_cls_or_mod, method_name, met, singleton_original_class = nil)
  singleton = !(singleton_original_class.nil?)

  # メソッド定義時のフックが無効化されている場合は何もしない
  return unless @__neuron_check_method_added_hook_enabled

  # 直前にNeuronCheck宣言部があれば、その宣言内容を登録する
  if (declaration = @__neuron_check_last_declaration) then

    # あらかじめ登録と、メソッドやクラスとの紐付けを行っておく
    # この処理を先に行わないと正しく名前を取得できない
    NeuronCheckSystem::METHOD_DECLARATIONS[target_cls_or_mod][method_name] = declaration
    declaration.assigned_class_or_module = target_cls_or_mod
    declaration.assigned_method = met
    declaration.assigned_singleton_original_class = singleton_original_class


    # 宣言に引数チェックが含まれている場合、宣言部の引数の数が、実際のメソッドの引数の数を超えていないかをチェック
    # 超えていれば宣言エラーとする
    if declaration.arg_matchers.size > met.parameters.size then
      raise NeuronCheckSystem::DeclarationError, "given arguments number of ##{method_name} greater than method definition - expected #{met.parameters.size} args, but #{declaration.arg_matchers.size} args were declared"
    end

    # パラメータの中にブロック型の引数が含まれているが
    # そのパラメータが、anyでもblockでもない型である場合はエラー
    met.parameters.each_with_index do |param_info, def_param_index|
      param_type, param_name = param_info
      if param_type == :block and (matcher = declaration.arg_matchers[def_param_index]) then
        next if matcher.respond_to?(:keyword_name) and matcher.keyword_name == 'any'
        next if matcher.respond_to?(:keyword_name) and matcher.keyword_name == 'block'

        context_caption = "#{NeuronCheckSystem::Utils.ordinalize(def_param_index + 1)} argument `#{param_name}' of `#{declaration.signature_caption_name_only}'"
        raise NeuronCheckSystem::DeclarationError, "#{context_caption} is block argument - it can be specified only keyword `any' or `block'"
      end
    end

    # 特異メソッドでなく、メソッド名が「initialize」であるにもかかわらず、returns宣言が含まれている場合はエラー
    if not singleton and method_name == :initialize and declaration.return_matcher then
      raise NeuronCheckSystem::DeclarationError, "returns declaration cannot be used with `#initialize' method"
    end

    # チェック処理の追加が完了したら、「最後に宣言した内容」を表すクラスインスタンス変数をnilに戻す
    @__neuron_check_last_declaration = nil
  end
end
attr(name, assignable = false) click to toggle source

属性定義

Calls superclass method
# File lib/neuroncheck/kernel.rb, line 13
def attr(name, assignable = false)
  @__neuron_check_method_added_hook_enabled = false
  begin
    # 元処理を呼ぶ
    super

    # NeuronCheck用の属性定義時処理を呼ぶ
    __neuron_check_attr_defined(__method__, [name], use_reader: true, use_writer: assignable)
  ensure
    @__neuron_check_method_added_hook_enabled = true
  end
end
attr_accessor(*names) click to toggle source
Calls superclass method
# File lib/neuroncheck/kernel.rb, line 52
def attr_accessor(*names)
  @__neuron_check_method_added_hook_enabled = false
  begin
    # 元処理を呼ぶ
    super

    # NeuronCheck用の属性定義時処理を呼ぶ
    __neuron_check_attr_defined(__method__, names, use_reader: true, use_writer: true)
  ensure
    @__neuron_check_method_added_hook_enabled = true
  end
end
attr_reader(*names) click to toggle source
Calls superclass method
# File lib/neuroncheck/kernel.rb, line 26
def attr_reader(*names)
  @__neuron_check_method_added_hook_enabled = false
  begin
    # 元処理を呼ぶ
    super

    # NeuronCheck用の属性定義時処理を呼ぶ
    __neuron_check_attr_defined(__method__, names, use_reader: true, use_writer: false)
  ensure
    @__neuron_check_method_added_hook_enabled = true
  end
end
attr_writer(*names) click to toggle source
Calls superclass method
# File lib/neuroncheck/kernel.rb, line 39
def attr_writer(*names)
  @__neuron_check_method_added_hook_enabled = false
  begin
    # 元処理を呼ぶ
    super

    # NeuronCheck用の属性定義時処理を呼ぶ
    __neuron_check_attr_defined(__method__, names, use_reader: false, use_writer: true)
  ensure
    @__neuron_check_method_added_hook_enabled = true
  end
end
method_added(name) click to toggle source

インスタンスメソッド定義を追加したときの処理

Calls superclass method
# File lib/neuroncheck/kernel.rb, line 141
def method_added(name)
  # まずは親処理を呼ぶ
  super

  # メイン処理をコール
  __neuron_check_method_added_hook(self, name, self.instance_method(name))
end
singleton_method_added(name) click to toggle source

特異メソッド定義を追加したときの処理

Calls superclass method
# File lib/neuroncheck/kernel.rb, line 150
def singleton_method_added(name)
  # まずは親処理を呼ぶ
  super

  # メイン処理をコール
  __neuron_check_method_added_hook(self.singleton_class, name, self.singleton_class.instance_method(name), self)
end