Module: NeuronCheckSystem::Kernel

Defined in:
lib/neuroncheck/kernel.rb

Overview

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

Instance Method Summary collapse

Instance Method Details

#__neuron_check_attr_defined(used_method_name, names, use_reader: false, use_writer: false) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# 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) ⇒ Object

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



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# 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) ⇒ Object

属性定義



13
14
15
16
17
18
19
20
21
22
23
24
# 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) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
# 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) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
# 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) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
# 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) ⇒ Object

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



141
142
143
144
145
146
147
# 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) ⇒ Object

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



150
151
152
153
154
155
156
# 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