neuroncheck 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,12 @@
1
1
  module NeuronCheckSystem
2
- class ExceptionBase < Exception
3
- end
2
+ class ExceptionBase < Exception
3
+ end
4
4
 
5
- class DeclarationError < ExceptionBase
6
- end
5
+ class DeclarationError < ExceptionBase
6
+ end
7
7
 
8
- class PluginError < ExceptionBase
9
- end
8
+ class PluginError < ExceptionBase
9
+ end
10
10
  end
11
11
 
12
12
  class NeuronCheckError < NeuronCheckSystem::ExceptionBase
@@ -6,613 +6,613 @@ require 'neuroncheck/plugin'
6
6
  require 'neuroncheck/syntax'
7
7
 
8
8
  module NeuronCheckSystem
9
- # メソッド追加時のフック処理や、属性宣言メソッドのオーバーライドなどを定義したメインモジュール
10
- # NeuronCheckを行いたい対象のモジュールやクラスにextendすることで使用する
11
- module Kernel
12
- # 属性定義
13
- def attr(name, assignable = false)
14
- @__neuron_check_method_added_hook_enabled = false
15
- begin
16
- # 元処理を呼ぶ
17
- super
18
-
19
- # NeuronCheck用の属性定義時処理を呼ぶ
20
- __neuron_check_attr_defined(__method__, [name], use_reader: true, use_writer: assignable)
21
- ensure
22
- @__neuron_check_method_added_hook_enabled = true
23
- end
24
- end
25
-
26
- def attr_reader(*names)
27
- @__neuron_check_method_added_hook_enabled = false
28
- begin
29
- # 元処理を呼ぶ
30
- super
31
-
32
- # NeuronCheck用の属性定義時処理を呼ぶ
33
- __neuron_check_attr_defined(__method__, names, use_reader: true, use_writer: false)
34
- ensure
35
- @__neuron_check_method_added_hook_enabled = true
36
- end
37
- end
38
-
39
- def attr_writer(*names)
40
- @__neuron_check_method_added_hook_enabled = false
41
- begin
42
- # 元処理を呼ぶ
43
- super
44
-
45
- # NeuronCheck用の属性定義時処理を呼ぶ
46
- __neuron_check_attr_defined(__method__, names, use_reader: false, use_writer: true)
47
- ensure
48
- @__neuron_check_method_added_hook_enabled = true
49
- end
50
- end
51
-
52
- def attr_accessor(*names)
53
- @__neuron_check_method_added_hook_enabled = false
54
- begin
55
- # 元処理を呼ぶ
56
- super
57
-
58
- # NeuronCheck用の属性定義時処理を呼ぶ
59
- __neuron_check_attr_defined(__method__, names, use_reader: true, use_writer: true)
60
- ensure
61
- @__neuron_check_method_added_hook_enabled = true
62
- end
63
- end
64
-
65
- def __neuron_check_attr_defined(used_method_name, names, use_reader: false, use_writer: false)
66
- # 直前にNeuronCheck宣言部があれば、その宣言内容を各メソッドへ登録する
67
- if (declaration = @__neuron_check_last_declaration) then
68
- # 短縮記法による宣言かどうかで分岐
69
- if declaration.shorthand then
70
- # 短縮記法の場合は、引数が2つ以上宣言されている、もしくは戻り値が宣言されている場合にエラーとする
71
- if declaration.arg_matchers.size >= 2 or declaration.return_matcher then
72
- raise NeuronCheckSystem::DeclarationError, "expected value must be one for `#{used_method_name}'", declaration.declared_caller_locations
73
- end
74
-
75
- # 引数1用のマッチャを属性用のマッチャとみなす
76
- target_attr_matcher = declaration.arg_matchers[0]
77
- else
78
- # 通常の宣言の場合、引数、戻り値の宣言がされている場合はエラーとする
79
- if declaration.arg_matchers.size >= 1 or declaration.return_matcher then
80
- raise NeuronCheckSystem::DeclarationError, "`args' or `returns' declaration can be used only for method definition, but used for `#{used_method_name}'", declaration.declared_caller_locations
81
- end
82
-
83
- target_attr_matcher = declaration.attr_matcher
84
- end
85
-
86
- # 属性チェック用モジュールに対する処理
87
- @__neuron_check_attr_check_module.module_eval do
88
-
89
- # 属性1つごとに処理
90
- names.each do |attr_name|
91
- # 属性チェック用モジュールに、readerチェック用のラッパーメソッドを追加する
92
- if use_reader then
93
- define_method(attr_name) do
94
- # 通常の処理を呼び出す
95
- val = super()
96
-
97
- # 属性宣言があればチェック処理
98
- if target_attr_matcher then
99
- unless target_attr_matcher.match?(val, self) then
100
- context_caption = "value of attribute `#{self.class.name}##{attr_name}'"
101
- raise NeuronCheckError, target_attr_matcher.get_error_message(declaration, context_caption, val), (NeuronCheck.debug? ? caller : caller(1))
102
- end
103
- end
104
- end
105
- end
106
-
107
- # 属性チェック用モジュールに、writerチェック用のラッパーメソッドを追加する
108
- if use_writer then
109
- define_method("#{attr_name}=") do |val|
110
- # 属性宣言があればチェック処理
111
- if target_attr_matcher then
112
- unless target_attr_matcher.match?(val, self) then
113
- context_caption = "value of attribute `#{self.class.name}##{attr_name}'"
114
- raise NeuronCheckError, target_attr_matcher.get_error_message(declaration, context_caption, val, phrase_after_but: 'set'), (NeuronCheck.debug? ? caller : caller(1))
115
- end
116
- end
117
-
118
- # 通常の処理を呼び出す
119
- super(val)
120
- end
121
- end
122
-
123
- end
124
- end
125
-
126
- # 属性1つごとに処理
127
- names.each do |attr_name|
128
- # 登録実行
129
- NeuronCheckSystem::ATTR_DECLARATIONS[self][attr_name] = declaration
130
- declaration.assigned_class_or_module = self
131
- declaration.assigned_attribute_name = attr_name
132
- end
133
-
134
- # チェック処理の追加が完了したら、「最後に宣言した内容」を表すクラスインスタンス変数をnilに戻す
135
- @__neuron_check_last_declaration = nil
136
- end
137
- end
138
-
139
-
140
- # インスタンスメソッド定義を追加したときの処理
141
- def method_added(name)
142
- # まずは親処理を呼ぶ
143
- super
144
-
145
- # メイン処理をコール
146
- __neuron_check_method_added_hook(self, name, self.instance_method(name))
147
- end
148
-
149
- # 特異メソッド定義を追加したときの処理
150
- def singleton_method_added(name)
151
- # まずは親処理を呼ぶ
152
- super
153
-
154
- # メイン処理をコール
155
- __neuron_check_method_added_hook(self.singleton_class, name, self.singleton_class.instance_method(name), self)
156
- end
157
-
158
- # メソッド/特異メソッドを定義したときの共通処理
159
- def __neuron_check_method_added_hook(target_cls_or_mod, method_name, met, singleton_original_class = nil)
160
- singleton = !(singleton_original_class.nil?)
161
-
162
- # メソッド定義時のフックが無効化されている場合は何もしない
163
- return unless @__neuron_check_method_added_hook_enabled
164
-
165
- # 直前にNeuronCheck宣言部があれば、その宣言内容を登録する
166
- if (declaration = @__neuron_check_last_declaration) then
167
-
168
- # あらかじめ登録と、メソッドやクラスとの紐付けを行っておく
169
- # この処理を先に行わないと正しく名前を取得できない
170
- NeuronCheckSystem::METHOD_DECLARATIONS[target_cls_or_mod][method_name] = declaration
171
- declaration.assigned_class_or_module = target_cls_or_mod
172
- declaration.assigned_method = met
173
- declaration.assigned_singleton_original_class = singleton_original_class
174
-
175
-
176
- # 宣言に引数チェックが含まれている場合、宣言部の引数の数が、実際のメソッドの引数の数を超えていないかをチェック
177
- # 超えていれば宣言エラーとする
178
- if declaration.arg_matchers.size > met.parameters.size then
179
- 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"
180
- end
181
-
182
- # パラメータの中にブロック型の引数が含まれているが
183
- # そのパラメータが、anyでもblockでもない型である場合はエラー
184
- met.parameters.each_with_index do |param_info, def_param_index|
185
- param_type, param_name = param_info
186
- if param_type == :block and (matcher = declaration.arg_matchers[def_param_index]) then
187
- next if matcher.respond_to?(:keyword_name) and matcher.keyword_name == 'any'
188
- next if matcher.respond_to?(:keyword_name) and matcher.keyword_name == 'block'
189
-
190
- context_caption = "#{NeuronCheckSystem::Utils.ordinalize(def_param_index + 1)} argument `#{param_name}' of `#{declaration.signature_caption_name_only}'"
191
- raise NeuronCheckSystem::DeclarationError, "#{context_caption} is block argument - it can be specified only keyword `any' or `block'"
192
- end
193
- end
194
-
195
- # 特異メソッドでなく、メソッド名が「initialize」であるにもかかわらず、returns宣言が含まれている場合はエラー
196
- if not singleton and method_name == :initialize and declaration.return_matcher then
197
- raise NeuronCheckSystem::DeclarationError, "returns declaration cannot be used with `#initialize' method"
198
- end
199
-
200
- # チェック処理の追加が完了したら、「最後に宣言した内容」を表すクラスインスタンス変数をnilに戻す
201
- @__neuron_check_last_declaration = nil
202
- end
203
- end
204
- end
205
-
206
-
207
- # チェック処理を差し込むためのTracePointを作成
208
- TRACE_POINT = TracePoint.new(:call, :return) do |tp|
209
- cls = tp.defined_class
210
-
211
- # メソッドに紐付けられた宣言を取得。取得できなければ終了
212
- decls = METHOD_DECLARATIONS[cls]
213
- next unless decls
214
-
215
- decl = decls[tp.method_id]
216
- next unless decl
217
-
218
- # メソッドの宣言情報を取得 (宣言があれば、メソッドの定義情報も必ず格納されているはずなので、取得成否チェックはなし)
219
- met = decl.assigned_method
220
-
221
- # ここからの処理はイベント種別で分岐
222
- case tp.event
223
- when :call # タイプが「メソッドの呼び出し」の場合の処理
224
- self.trace_method_call(tp.self, decl, met, tp.binding)
225
- when :return # タイプが「メソッドの終了」の場合の処理
226
-
227
- # 「次の1回はスキップする」フラグがONであればスキップ
228
- if @skip_next_return then
229
- @skip_next_return = false
230
- next
231
- else
232
-
233
- ex = self.trace_method_return(tp.self, decl, met, tp.binding, tp.return_value)
234
- if ex then
235
- # raiseで例外を脱出した場合、なぜかもう1回returnイベントが呼ばれるため、次の1回はスキップするように設定
236
- @skip_next_return = true
237
-
238
- # 例外を発生させる
239
- raise ex
240
- end
241
- end
242
- end
243
- end
244
- TRACE_POINT.enable
245
-
246
- # 全メソッド宣言のリスト
247
- METHOD_DECLARATIONS = {}
248
-
249
- # 全属性制限と対応するメソッド情報のリスト
250
- ATTR_DECLARATIONS = {}
251
-
252
- # bindingクラスに local_variable_get が定義されているかどうかを判定し、その判定結果を定数に記録 (Ruby 2.1以降では定義されているはず)
253
- LOCAL_VARIABLE_GET_USABLE = (binding.respond_to?(:local_variable_get))
254
-
255
- # 指定したbindingから、指定した名前のローカル変数を取得する (Binding#local_variable_getが定義されているかどうかで実装を変える)
256
- def self.get_local_variable_from_binding(target_binding, var_name)
257
- if LOCAL_VARIABLE_GET_USABLE then
258
- target_binding.local_variable_get(var_name)
259
- else
260
- # local_variable_getが使えない時は、代わりにevalを使用して取得
261
- target_binding.eval(var_name.to_s)
262
- end
263
- end
264
-
265
- # メソッド呼び出し時のチェック処理 (現段階ではattrメソッドの呼び出しでは発生しないことに注意。Ruby 2.1で確認)
266
- def self.trace_method_call(method_self, declaration, met, method_binding)
267
- param_values = {}
268
-
269
- # 大域脱出できるようにcatchを使用 (バックトレースを正しく取得するための処置)
270
- error_msg = catch(:neuron_check_error_tag) do
271
-
272
- # 対象メソッドの引数1つごとに処理
273
- met.parameters.each_with_index do |param_info, def_param_index|
274
- param_type, param_name = param_info
275
-
276
- # 実際に渡された引数の値を取得 (デフォルト値の処理も考慮する)
277
- param_value = get_local_variable_from_binding(method_binding, param_name)
278
- param_values[param_name] = param_value
279
-
280
- # 指定位置に対応するマッチャが登録されている場合のみ処理
281
- if (matcher = declaration.arg_matchers[def_param_index]) then
282
-
283
- # 引数の種類で処理を分岐
284
- case param_type
285
- when :key # キーワード引数
286
- unless matcher.match?(param_value, self) then
287
- context_caption = "argument `#{param_name}' of `#{declaration.signature_caption_name_only}'"
288
- @last_argument_error_info = [method_self, met.name] # 引数チェックエラーの発生情報を記録
289
-
290
- throw :neuron_check_error_tag, matcher.get_error_message(declaration, context_caption, param_value)
291
- end
292
-
293
- when :keyrest # キーワード引数の可変長部分
294
- # 可変長部分1つごとにチェック
295
- param_value.each_pair do |key, value|
296
- unless matcher.match?(value, self) then
297
- context_caption = "argument `#{param_name}' of `#{declaration.signature_caption_name_only}'"
298
- @last_argument_error_info = [method_self, met.name] # 引数チェックエラーの発生情報を記録
299
-
300
- throw :neuron_check_error_tag, matcher.get_error_message(declaration, context_caption, value)
301
- end
302
- end
303
-
304
- when :rest # 可変長部分
305
- # 可変長引数であれば、受け取った引数1つごとにチェック
306
- param_value.each_with_index do |val, i|
307
- unless matcher.match?(val, self) then
308
- context_caption = "#{NeuronCheckSystem::Utils.ordinalize(def_param_index + 1 + i)} argument `#{param_name}' of `#{declaration.signature_caption_name_only}'"
309
- @last_argument_error_info = [method_self, met.name] # 引数チェックエラーの発生情報を記録
310
-
311
- throw :neuron_check_error_tag, matcher.get_error_message(declaration, context_caption, val)
312
- end
313
- end
314
-
315
- else # 通常の引数/ブロック引数
316
- unless matcher.match?(param_value, self) then
317
- context_caption = "#{NeuronCheckSystem::Utils.ordinalize(def_param_index + 1)} argument `#{param_name}' of `#{declaration.signature_caption_name_only}'"
318
- @last_argument_error_info = [method_self, met.name] # 引数チェックエラーの発生情報を記録
319
-
320
- throw :neuron_check_error_tag, matcher.get_error_message(declaration, context_caption, param_value)
321
- end
322
- end
323
- end
324
- end
325
-
326
-
327
- # 事前条件があれば実行
328
- if declaration.precond then
329
- # コンテキストを生成
330
- context = make_cond_block_context(method_self, 'precond', param_values, declaration.precond_allow_instance_method)
331
-
332
- # 条件文実行
333
- context.instance_exec(&(declaration.precond))
334
- end
335
-
336
- # 最後まで正常実行した場合はnilを返す
337
- nil
338
- end # catch
339
-
340
- # エラーメッセージがあればNeuronCheckError発生
341
- if error_msg then
342
- raise NeuronCheckError, error_msg, (NeuronCheck.debug? ? caller : caller(3))
343
- end
344
- end
345
-
346
- # メソッド終了時のチェック処理
347
- def self.trace_method_return(method_self, declaration, met, method_binding, return_value)
348
- # 処理中に例外が発生した場合、TracePointがreturnとして検知してしまうため
349
- # それを防ぐために例外を自前でキャッチ
350
- begin
351
- # 大域脱出できるようにcatchを使用 (バックトレースを正しく取得するための処置)
352
- error_msg = catch(:neuron_check_error_tag) do
353
-
354
- # 最後に引数チェックエラーが発生しており、selfとメソッド名が同じものであれば、return値のチェックをスキップ
355
- # (NeuronCheckError例外を発生させたときにもreturnイベントは発生してしまうため、その対策として)
356
- if @last_argument_error_info and @last_argument_error_info[0].equal?(method_self) and @last_argument_error_info[1] == met.name then
357
- @last_argument_error_info = nil
358
- return
359
- end
360
-
361
- param_values = {}
362
-
363
- # 対象メソッドの引数1つごとに処理
364
- met.parameters.each_with_index do |param_info, def_param_index|
365
- _, param_name = param_info
366
-
367
- # 実際に渡された引数の値を取得 (デフォルト値の処理も考慮する)
368
- param_value = get_local_variable_from_binding(method_binding, param_name)
369
- param_values[param_name] = param_value
370
- end
371
-
372
- # 戻り値チェック
373
- if (matcher = declaration.return_matcher) then
374
- # チェック処理
375
- unless matcher.match?(return_value, method_self) then
376
- # エラー
377
- throw :neuron_check_error_tag, matcher.get_error_message(declaration, "return value of `#{declaration.signature_caption_name_only}'", return_value)
378
- end
379
- end
380
-
381
- # 事後条件があれば実行
382
- if declaration.postcond then
383
- # コンテキストを生成
384
- context = make_cond_block_context(method_self, 'postcond', param_values, declaration.postcond_allow_instance_method)
385
-
386
- # 条件文実行
387
- context.instance_exec(return_value, &(declaration.postcond))
388
- end
389
-
390
- # 最後まで正常実行した場合はnilを返す
391
- nil
392
- end # catch
393
-
394
- # エラーメッセージがあればNeuronCheckError発生
395
- if error_msg then
396
- raise NeuronCheckError, error_msg, (NeuronCheck.debug? ? caller : caller(3))
397
- end
398
- rescue Exception
399
- # 例外が発生した場合はその例外オブジェクトを返す
400
- return $!
401
- end
402
- end
403
-
404
- # 事前条件/事後条件の実行用コンテキストを構築する
405
- def self.make_cond_block_context(method_self, context_caption, param_values, allow_instance_method)
406
- # ローカル変数指定用の無名モジュールを作成
407
- local_mod = Module.new
408
- local_mod.module_eval do
409
- # ローカル変数取得用のメソッドを定義する
410
- param_values.each_pair do |var_name, value|
411
- define_method(var_name) do
412
- return value
413
- end
414
- end
415
- end
416
-
417
- # ブロック実行用のコンテキストを生成し、ローカル変数保持モジュールを差し込む
418
- context = CondBlockContext.new(context_caption, method_self, allow_instance_method)
419
- context.extend local_mod
420
-
421
- # 対象のオブジェクトが持つ全てのインスタンス変数を、コンテキストへ引き渡す
422
- method_self.instance_variables.each do |var_name|
423
- val = method_self.instance_variable_get(var_name)
424
- context.instance_variable_set(var_name, val)
425
- end
426
-
427
- return context
428
- end
429
-
430
- # モジュール/クラス1つに対して、NeuronCheck用の初期化を行う
431
- def self.initialize_module_for_neuron_check(mod_or_class)
432
- # 2回目以降の初期化であれば何もせずにスルー
433
- if mod_or_class.instance_variable_get(:@__neuron_check_initialized) then
434
- return
435
- end
436
-
437
- # 宣言とメソッド情報を格納するためのHashを更新
438
- NeuronCheckSystem::METHOD_DECLARATIONS[mod_or_class] = {}
439
- NeuronCheckSystem::METHOD_DECLARATIONS[mod_or_class.singleton_class] = {} # 特異メソッド用のクラスも追加
440
- NeuronCheckSystem::ATTR_DECLARATIONS[mod_or_class] = {}
441
-
442
- # 対象のModule/Classに対する処理
443
- mod_or_class.instance_eval do
444
- # 最後に宣言された内容を保持するクラスインスタンス変数(モジュールインスタンス変数)を定義
445
- @__neuron_check_last_declaration = nil
446
- # メソッド定義時のフックを一時的に無効化するフラグ
447
- @__neuron_check_method_added_hook_enabled = true
448
- # 属性の処理を上書きするために差し込む無名モジュールを定義 (prependする)
449
- @__neuron_check_attr_check_module = Module.new
450
- prepend @__neuron_check_attr_check_module
451
-
452
- # メソッド定義時のフックなどを定義したモジュールをextend
453
- extend NeuronCheckSystem::Kernel
454
-
455
- # initialize済みフラグON
456
- @__neuron_check_initialized = true # 初期化
457
- end
458
- end
9
+ # メソッド追加時のフック処理や、属性宣言メソッドのオーバーライドなどを定義したメインモジュール
10
+ # NeuronCheckを行いたい対象のモジュールやクラスにextendすることで使用する
11
+ module Kernel
12
+ # 属性定義
13
+ def attr(name, assignable = false)
14
+ @__neuron_check_method_added_hook_enabled = false
15
+ begin
16
+ # 元処理を呼ぶ
17
+ super
18
+
19
+ # NeuronCheck用の属性定義時処理を呼ぶ
20
+ __neuron_check_attr_defined(__method__, [name], use_reader: true, use_writer: assignable)
21
+ ensure
22
+ @__neuron_check_method_added_hook_enabled = true
23
+ end
24
+ end
25
+
26
+ def attr_reader(*names)
27
+ @__neuron_check_method_added_hook_enabled = false
28
+ begin
29
+ # 元処理を呼ぶ
30
+ super
31
+
32
+ # NeuronCheck用の属性定義時処理を呼ぶ
33
+ __neuron_check_attr_defined(__method__, names, use_reader: true, use_writer: false)
34
+ ensure
35
+ @__neuron_check_method_added_hook_enabled = true
36
+ end
37
+ end
38
+
39
+ def attr_writer(*names)
40
+ @__neuron_check_method_added_hook_enabled = false
41
+ begin
42
+ # 元処理を呼ぶ
43
+ super
44
+
45
+ # NeuronCheck用の属性定義時処理を呼ぶ
46
+ __neuron_check_attr_defined(__method__, names, use_reader: false, use_writer: true)
47
+ ensure
48
+ @__neuron_check_method_added_hook_enabled = true
49
+ end
50
+ end
51
+
52
+ def attr_accessor(*names)
53
+ @__neuron_check_method_added_hook_enabled = false
54
+ begin
55
+ # 元処理を呼ぶ
56
+ super
57
+
58
+ # NeuronCheck用の属性定義時処理を呼ぶ
59
+ __neuron_check_attr_defined(__method__, names, use_reader: true, use_writer: true)
60
+ ensure
61
+ @__neuron_check_method_added_hook_enabled = true
62
+ end
63
+ end
64
+
65
+ def __neuron_check_attr_defined(used_method_name, names, use_reader: false, use_writer: false)
66
+ # 直前にNeuronCheck宣言部があれば、その宣言内容を各メソッドへ登録する
67
+ if (declaration = @__neuron_check_last_declaration) then
68
+ # 短縮記法による宣言かどうかで分岐
69
+ if declaration.shorthand then
70
+ # 短縮記法の場合は、引数が2つ以上宣言されている、もしくは戻り値が宣言されている場合にエラーとする
71
+ if declaration.arg_matchers.size >= 2 or declaration.return_matcher then
72
+ raise NeuronCheckSystem::DeclarationError, "expected value must be one for `#{used_method_name}'", declaration.declared_caller_locations
73
+ end
74
+
75
+ # 引数1用のマッチャを属性用のマッチャとみなす
76
+ target_attr_matcher = declaration.arg_matchers[0]
77
+ else
78
+ # 通常の宣言の場合、引数、戻り値の宣言がされている場合はエラーとする
79
+ if declaration.arg_matchers.size >= 1 or declaration.return_matcher then
80
+ raise NeuronCheckSystem::DeclarationError, "`args' or `returns' declaration can be used only for method definition, but used for `#{used_method_name}'", declaration.declared_caller_locations
81
+ end
82
+
83
+ target_attr_matcher = declaration.attr_matcher
84
+ end
85
+
86
+ # 属性チェック用モジュールに対する処理
87
+ @__neuron_check_attr_check_module.module_eval do
88
+
89
+ # 属性1つごとに処理
90
+ names.each do |attr_name|
91
+ # 属性チェック用モジュールに、readerチェック用のラッパーメソッドを追加する
92
+ if use_reader then
93
+ define_method(attr_name) do
94
+ # 通常の処理を呼び出す
95
+ val = super()
96
+
97
+ # 属性宣言があればチェック処理
98
+ if target_attr_matcher then
99
+ unless target_attr_matcher.match?(val, self) then
100
+ context_caption = "value of attribute `#{self.class.name}##{attr_name}'"
101
+ raise NeuronCheckError, target_attr_matcher.get_error_message(declaration, context_caption, val), (NeuronCheck.debug? ? caller : caller(1))
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ # 属性チェック用モジュールに、writerチェック用のラッパーメソッドを追加する
108
+ if use_writer then
109
+ define_method("#{attr_name}=") do |val|
110
+ # 属性宣言があればチェック処理
111
+ if target_attr_matcher then
112
+ unless target_attr_matcher.match?(val, self) then
113
+ context_caption = "value of attribute `#{self.class.name}##{attr_name}'"
114
+ raise NeuronCheckError, target_attr_matcher.get_error_message(declaration, context_caption, val, phrase_after_but: 'set'), (NeuronCheck.debug? ? caller : caller(1))
115
+ end
116
+ end
117
+
118
+ # 通常の処理を呼び出す
119
+ super(val)
120
+ end
121
+ end
122
+
123
+ end
124
+ end
125
+
126
+ # 属性1つごとに処理
127
+ names.each do |attr_name|
128
+ # 登録実行
129
+ NeuronCheckSystem::ATTR_DECLARATIONS[self][attr_name] = declaration
130
+ declaration.assigned_class_or_module = self
131
+ declaration.assigned_attribute_name = attr_name
132
+ end
133
+
134
+ # チェック処理の追加が完了したら、「最後に宣言した内容」を表すクラスインスタンス変数をnilに戻す
135
+ @__neuron_check_last_declaration = nil
136
+ end
137
+ end
138
+
139
+
140
+ # インスタンスメソッド定義を追加したときの処理
141
+ def method_added(name)
142
+ # まずは親処理を呼ぶ
143
+ super
144
+
145
+ # メイン処理をコール
146
+ __neuron_check_method_added_hook(self, name, self.instance_method(name))
147
+ end
148
+
149
+ # 特異メソッド定義を追加したときの処理
150
+ def singleton_method_added(name)
151
+ # まずは親処理を呼ぶ
152
+ super
153
+
154
+ # メイン処理をコール
155
+ __neuron_check_method_added_hook(self.singleton_class, name, self.singleton_class.instance_method(name), self)
156
+ end
157
+
158
+ # メソッド/特異メソッドを定義したときの共通処理
159
+ def __neuron_check_method_added_hook(target_cls_or_mod, method_name, met, singleton_original_class = nil)
160
+ singleton = !(singleton_original_class.nil?)
161
+
162
+ # メソッド定義時のフックが無効化されている場合は何もしない
163
+ return unless @__neuron_check_method_added_hook_enabled
164
+
165
+ # 直前にNeuronCheck宣言部があれば、その宣言内容を登録する
166
+ if (declaration = @__neuron_check_last_declaration) then
167
+
168
+ # あらかじめ登録と、メソッドやクラスとの紐付けを行っておく
169
+ # この処理を先に行わないと正しく名前を取得できない
170
+ NeuronCheckSystem::METHOD_DECLARATIONS[target_cls_or_mod][method_name] = declaration
171
+ declaration.assigned_class_or_module = target_cls_or_mod
172
+ declaration.assigned_method = met
173
+ declaration.assigned_singleton_original_class = singleton_original_class
174
+
175
+
176
+ # 宣言に引数チェックが含まれている場合、宣言部の引数の数が、実際のメソッドの引数の数を超えていないかをチェック
177
+ # 超えていれば宣言エラーとする
178
+ if declaration.arg_matchers.size > met.parameters.size then
179
+ 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"
180
+ end
181
+
182
+ # パラメータの中にブロック型の引数が含まれているが
183
+ # そのパラメータが、anyでもblockでもない型である場合はエラー
184
+ met.parameters.each_with_index do |param_info, def_param_index|
185
+ param_type, param_name = param_info
186
+ if param_type == :block and (matcher = declaration.arg_matchers[def_param_index]) then
187
+ next if matcher.respond_to?(:keyword_name) and matcher.keyword_name == 'any'
188
+ next if matcher.respond_to?(:keyword_name) and matcher.keyword_name == 'block'
189
+
190
+ context_caption = "#{NeuronCheckSystem::Utils.ordinalize(def_param_index + 1)} argument `#{param_name}' of `#{declaration.signature_caption_name_only}'"
191
+ raise NeuronCheckSystem::DeclarationError, "#{context_caption} is block argument - it can be specified only keyword `any' or `block'"
192
+ end
193
+ end
194
+
195
+ # 特異メソッドでなく、メソッド名が「initialize」であるにもかかわらず、returns宣言が含まれている場合はエラー
196
+ if not singleton and method_name == :initialize and declaration.return_matcher then
197
+ raise NeuronCheckSystem::DeclarationError, "returns declaration cannot be used with `#initialize' method"
198
+ end
199
+
200
+ # チェック処理の追加が完了したら、「最後に宣言した内容」を表すクラスインスタンス変数をnilに戻す
201
+ @__neuron_check_last_declaration = nil
202
+ end
203
+ end
204
+ end
205
+
206
+
207
+ # チェック処理を差し込むためのTracePointを作成
208
+ TRACE_POINT = TracePoint.new(:call, :return) do |tp|
209
+ cls = tp.defined_class
210
+
211
+ # メソッドに紐付けられた宣言を取得。取得できなければ終了
212
+ decls = METHOD_DECLARATIONS[cls]
213
+ next unless decls
214
+
215
+ decl = decls[tp.method_id]
216
+ next unless decl
217
+
218
+ # メソッドの宣言情報を取得 (宣言があれば、メソッドの定義情報も必ず格納されているはずなので、取得成否チェックはなし)
219
+ met = decl.assigned_method
220
+
221
+ # ここからの処理はイベント種別で分岐
222
+ case tp.event
223
+ when :call # タイプが「メソッドの呼び出し」の場合の処理
224
+ self.trace_method_call(tp.self, decl, met, tp.binding)
225
+ when :return # タイプが「メソッドの終了」の場合の処理
226
+
227
+ # 「次の1回はスキップする」フラグがONであればスキップ
228
+ if @skip_next_return then
229
+ @skip_next_return = false
230
+ next
231
+ else
232
+
233
+ ex = self.trace_method_return(tp.self, decl, met, tp.binding, tp.return_value)
234
+ if ex then
235
+ # raiseで例外を脱出した場合、なぜかもう1回returnイベントが呼ばれるため、次の1回はスキップするように設定
236
+ @skip_next_return = true
237
+
238
+ # 例外を発生させる
239
+ raise ex
240
+ end
241
+ end
242
+ end
243
+ end
244
+ TRACE_POINT.enable
245
+
246
+ # 全メソッド宣言のリスト
247
+ METHOD_DECLARATIONS = {}
248
+
249
+ # 全属性制限と対応するメソッド情報のリスト
250
+ ATTR_DECLARATIONS = {}
251
+
252
+ # bindingクラスに local_variable_get が定義されているかどうかを判定し、その判定結果を定数に記録 (Ruby 2.1以降では定義されているはず)
253
+ LOCAL_VARIABLE_GET_USABLE = (binding.respond_to?(:local_variable_get))
254
+
255
+ # 指定したbindingから、指定した名前のローカル変数を取得する (Binding#local_variable_getが定義されているかどうかで実装を変える)
256
+ def self.get_local_variable_from_binding(target_binding, var_name)
257
+ if LOCAL_VARIABLE_GET_USABLE then
258
+ target_binding.local_variable_get(var_name)
259
+ else
260
+ # local_variable_getが使えない時は、代わりにevalを使用して取得
261
+ target_binding.eval(var_name.to_s)
262
+ end
263
+ end
264
+
265
+ # メソッド呼び出し時のチェック処理 (現段階ではattrメソッドの呼び出しでは発生しないことに注意。Ruby 2.1で確認)
266
+ def self.trace_method_call(method_self, declaration, met, method_binding)
267
+ param_values = {}
268
+
269
+ # 大域脱出できるようにcatchを使用 (バックトレースを正しく取得するための処置)
270
+ error_msg = catch(:neuron_check_error_tag) do
271
+
272
+ # 対象メソッドの引数1つごとに処理
273
+ met.parameters.each_with_index do |param_info, def_param_index|
274
+ param_type, param_name = param_info
275
+
276
+ # 実際に渡された引数の値を取得 (デフォルト値の処理も考慮する)
277
+ param_value = get_local_variable_from_binding(method_binding, param_name)
278
+ param_values[param_name] = param_value
279
+
280
+ # 指定位置に対応するマッチャが登録されている場合のみ処理
281
+ if (matcher = declaration.arg_matchers[def_param_index]) then
282
+
283
+ # 引数の種類で処理を分岐
284
+ case param_type
285
+ when :key # キーワード引数
286
+ unless matcher.match?(param_value, self) then
287
+ context_caption = "argument `#{param_name}' of `#{declaration.signature_caption_name_only}'"
288
+ @last_argument_error_info = [method_self, met.name] # 引数チェックエラーの発生情報を記録
289
+
290
+ throw :neuron_check_error_tag, matcher.get_error_message(declaration, context_caption, param_value)
291
+ end
292
+
293
+ when :keyrest # キーワード引数の可変長部分
294
+ # 可変長部分1つごとにチェック
295
+ param_value.each_pair do |key, value|
296
+ unless matcher.match?(value, self) then
297
+ context_caption = "argument `#{param_name}' of `#{declaration.signature_caption_name_only}'"
298
+ @last_argument_error_info = [method_self, met.name] # 引数チェックエラーの発生情報を記録
299
+
300
+ throw :neuron_check_error_tag, matcher.get_error_message(declaration, context_caption, value)
301
+ end
302
+ end
303
+
304
+ when :rest # 可変長部分
305
+ # 可変長引数であれば、受け取った引数1つごとにチェック
306
+ param_value.each_with_index do |val, i|
307
+ unless matcher.match?(val, self) then
308
+ context_caption = "#{NeuronCheckSystem::Utils.ordinalize(def_param_index + 1 + i)} argument `#{param_name}' of `#{declaration.signature_caption_name_only}'"
309
+ @last_argument_error_info = [method_self, met.name] # 引数チェックエラーの発生情報を記録
310
+
311
+ throw :neuron_check_error_tag, matcher.get_error_message(declaration, context_caption, val)
312
+ end
313
+ end
314
+
315
+ else # 通常の引数/ブロック引数
316
+ unless matcher.match?(param_value, self) then
317
+ context_caption = "#{NeuronCheckSystem::Utils.ordinalize(def_param_index + 1)} argument `#{param_name}' of `#{declaration.signature_caption_name_only}'"
318
+ @last_argument_error_info = [method_self, met.name] # 引数チェックエラーの発生情報を記録
319
+
320
+ throw :neuron_check_error_tag, matcher.get_error_message(declaration, context_caption, param_value)
321
+ end
322
+ end
323
+ end
324
+ end
325
+
326
+
327
+ # 事前条件があれば実行
328
+ if declaration.precond then
329
+ # コンテキストを生成
330
+ context = make_cond_block_context(method_self, 'precond', param_values, declaration.precond_allow_instance_method)
331
+
332
+ # 条件文実行
333
+ context.instance_exec(&(declaration.precond))
334
+ end
335
+
336
+ # 最後まで正常実行した場合はnilを返す
337
+ nil
338
+ end # catch
339
+
340
+ # エラーメッセージがあればNeuronCheckError発生
341
+ if error_msg then
342
+ raise NeuronCheckError, error_msg, (NeuronCheck.debug? ? caller : caller(3))
343
+ end
344
+ end
345
+
346
+ # メソッド終了時のチェック処理
347
+ def self.trace_method_return(method_self, declaration, met, method_binding, return_value)
348
+ # 処理中に例外が発生した場合、TracePointがreturnとして検知してしまうため
349
+ # それを防ぐために例外を自前でキャッチ
350
+ begin
351
+ # 大域脱出できるようにcatchを使用 (バックトレースを正しく取得するための処置)
352
+ error_msg = catch(:neuron_check_error_tag) do
353
+
354
+ # 最後に引数チェックエラーが発生しており、selfとメソッド名が同じものであれば、return値のチェックをスキップ
355
+ # (NeuronCheckError例外を発生させたときにもreturnイベントは発生してしまうため、その対策として)
356
+ if @last_argument_error_info and @last_argument_error_info[0].equal?(method_self) and @last_argument_error_info[1] == met.name then
357
+ @last_argument_error_info = nil
358
+ return
359
+ end
360
+
361
+ param_values = {}
362
+
363
+ # 対象メソッドの引数1つごとに処理
364
+ met.parameters.each_with_index do |param_info, def_param_index|
365
+ _, param_name = param_info
366
+
367
+ # 実際に渡された引数の値を取得 (デフォルト値の処理も考慮する)
368
+ param_value = get_local_variable_from_binding(method_binding, param_name)
369
+ param_values[param_name] = param_value
370
+ end
371
+
372
+ # 戻り値チェック
373
+ if (matcher = declaration.return_matcher) then
374
+ # チェック処理
375
+ unless matcher.match?(return_value, method_self) then
376
+ # エラー
377
+ throw :neuron_check_error_tag, matcher.get_error_message(declaration, "return value of `#{declaration.signature_caption_name_only}'", return_value)
378
+ end
379
+ end
380
+
381
+ # 事後条件があれば実行
382
+ if declaration.postcond then
383
+ # コンテキストを生成
384
+ context = make_cond_block_context(method_self, 'postcond', param_values, declaration.postcond_allow_instance_method)
385
+
386
+ # 条件文実行
387
+ context.instance_exec(return_value, &(declaration.postcond))
388
+ end
389
+
390
+ # 最後まで正常実行した場合はnilを返す
391
+ nil
392
+ end # catch
393
+
394
+ # エラーメッセージがあればNeuronCheckError発生
395
+ if error_msg then
396
+ raise NeuronCheckError, error_msg, (NeuronCheck.debug? ? caller : caller(3))
397
+ end
398
+ rescue Exception
399
+ # 例外が発生した場合はその例外オブジェクトを返す
400
+ return $!
401
+ end
402
+ end
403
+
404
+ # 事前条件/事後条件の実行用コンテキストを構築する
405
+ def self.make_cond_block_context(method_self, context_caption, param_values, allow_instance_method)
406
+ # ローカル変数指定用の無名モジュールを作成
407
+ local_mod = Module.new
408
+ local_mod.module_eval do
409
+ # ローカル変数取得用のメソッドを定義する
410
+ param_values.each_pair do |var_name, value|
411
+ define_method(var_name) do
412
+ return value
413
+ end
414
+ end
415
+ end
416
+
417
+ # ブロック実行用のコンテキストを生成し、ローカル変数保持モジュールを差し込む
418
+ context = CondBlockContext.new(context_caption, method_self, allow_instance_method)
419
+ context.extend local_mod
420
+
421
+ # 対象のオブジェクトが持つ全てのインスタンス変数を、コンテキストへ引き渡す
422
+ method_self.instance_variables.each do |var_name|
423
+ val = method_self.instance_variable_get(var_name)
424
+ context.instance_variable_set(var_name, val)
425
+ end
426
+
427
+ return context
428
+ end
429
+
430
+ # モジュール/クラス1つに対して、NeuronCheck用の初期化を行う
431
+ def self.initialize_module_for_neuron_check(mod_or_class)
432
+ # 2回目以降の初期化であれば何もせずにスルー
433
+ if mod_or_class.instance_variable_get(:@__neuron_check_initialized) then
434
+ return
435
+ end
436
+
437
+ # 宣言とメソッド情報を格納するためのHashを更新
438
+ NeuronCheckSystem::METHOD_DECLARATIONS[mod_or_class] = {}
439
+ NeuronCheckSystem::METHOD_DECLARATIONS[mod_or_class.singleton_class] = {} # 特異メソッド用のクラスも追加
440
+ NeuronCheckSystem::ATTR_DECLARATIONS[mod_or_class] = {}
441
+
442
+ # 対象のModule/Classに対する処理
443
+ mod_or_class.instance_eval do
444
+ # 最後に宣言された内容を保持するクラスインスタンス変数(モジュールインスタンス変数)を定義
445
+ @__neuron_check_last_declaration = nil
446
+ # メソッド定義時のフックを一時的に無効化するフラグ
447
+ @__neuron_check_method_added_hook_enabled = true
448
+ # 属性の処理を上書きするために差し込む無名モジュールを定義 (prependする)
449
+ @__neuron_check_attr_check_module = Module.new
450
+ prepend @__neuron_check_attr_check_module
451
+
452
+ # メソッド定義時のフックなどを定義したモジュールをextend
453
+ extend NeuronCheckSystem::Kernel
454
+
455
+ # initialize済みフラグON
456
+ @__neuron_check_initialized = true # 初期化
457
+ end
458
+ end
459
459
  end
460
460
 
461
461
  # ユーザーが利用するメインモジュール。extend処理や、有効化/無効化などに使用する
462
462
  module NeuronCheck
463
463
 
464
- # チェック実行フラグ
465
- @enabled = true
466
- def self.enabled?; @enabled; end
467
-
468
- # デバッグフラグ
469
- @debug = false
470
- def self.debug; @debug; end
471
- def self.debug?; @debug; end
472
- def self.debug=(v); @debug = v; end
473
-
474
- # 無効化
475
- def self.disable
476
- if block_given? then
477
- # ブロックが渡された場合は、ブロック実行中のみチェックを無効化
478
- begin
479
- disable
480
- yield
481
- ensure
482
- enable
483
- end
484
- else
485
- @enabled = false
486
- NeuronCheckSystem::TRACE_POINT.disable # メソッド呼び出しフックを無効化
487
- end
488
- end
489
-
490
- # 無効化されたチェックを、再度有効化
491
- def self.enable
492
- if block_given? then
493
- # ブロックが渡された場合は、ブロック実行中のみチェックを有効か
494
- begin
495
- enable
496
- yield
497
- ensure
498
- disable
499
- end
500
- else
501
- @enabled = true
502
- NeuronCheckSystem::TRACE_POINT.enable # メソッド呼び出しフックを有効化
503
- end
504
- end
505
-
506
- # 単体チェック
507
- def self.match?(value, &expected_block)
508
- # 宣言ブロック実行用のコンテキストを作成
509
- context = NeuronCheckSystem::DeclarationContext.new
510
- # 宣言ブロックの内容を実行
511
- expected = context.instance_eval(&expected_block)
512
-
513
- matcher = NeuronCheckSystem.get_appropriate_matcher(expected, [])
514
- return matcher.match?(value, nil)
515
- end
516
-
517
- # 単体チェック
518
- def self.check(value, &expected_block)
519
- # 宣言ブロック実行用のコンテキストを作成
520
- context = NeuronCheckSystem::DeclarationContext.new
521
- # 宣言ブロックの内容を実行
522
- expected = context.instance_eval(&expected_block)
523
-
524
- matcher = NeuronCheckSystem.get_appropriate_matcher(expected, [])
525
- unless matcher.match?(value, nil) then
526
- raise NeuronCheckError, matcher.get_error_message(nil, 'value', value), (NeuronCheck.debug? ? caller : caller(1))
527
- end
528
- end
529
-
530
- # 宣言情報の出力
531
- def self.get_declarations_as_json(ignore_unnamed_modules: false)
532
- re = {'instance_methods' => {}, 'singleton_methods' => {}, 'attributes' => {}}
533
-
534
- NeuronCheckSystem::METHOD_DECLARATIONS.each_pair do |cls_or_mod, data|
535
- data.each_pair do |method_name, decl|
536
- method_type = (decl.assinged_to_singleton_method? ? 'singleton_methods' : 'instance_methods')
537
- target_mod_or_class = (decl.assinged_to_singleton_method? ? decl.assigned_singleton_original_class : decl.assigned_class_or_module)
538
- key = target_mod_or_class.name
539
-
540
- # 無名モジュール/クラスの場合の対応
541
- if key.nil? and not ignore_unnamed_modules then
542
- key = target_mod_or_class.inspect
543
- end
544
-
545
- if key then
546
- re[method_type][key] ||= {}
547
- re[method_type][key][method_name.to_s] = decl.meta_info_as_json
548
- end
549
- end
550
- end
551
-
552
- NeuronCheckSystem::ATTR_DECLARATIONS.each_pair do |cls_or_mod, data|
553
- key = cls_or_mod.name
554
-
555
- # 無名モジュール/クラスの場合の対応
556
- if key.nil? and not ignore_unnamed_modules then
557
- key = cls_or_mod.inspect
558
- end
559
-
560
- data.each_pair do |method_name, decl|
561
- re['attributes'][key] ||= {}
562
- re['attributes'][key][method_name.to_s] = decl.meta_info_as_json
563
- end
564
- end
565
-
566
- re
567
- end
568
-
569
- # extend時処理
570
- def self.extended(mod_or_class)
571
- # extend対象がモジュールでもクラスでもなければエラー
572
- unless mod_or_class.kind_of?(Module) then
573
- raise ScriptError, "NeuronCheck can be extended only to Class or Module"
574
- end
575
-
576
- # 2回目以降のextendであれば何もせずにスルー
577
- if mod_or_class.instance_variable_get(:@__neuron_check_extended) then
578
- return
579
- end
580
-
581
- # まずはNeuronCheck用の初期化
582
- NeuronCheckSystem.initialize_module_for_neuron_check(mod_or_class)
583
-
584
- # 対象のModule/Classに対する処理
585
- mod_or_class.instance_eval do
586
- # Module/Classに対して宣言用のメソッドを追加する
587
- extend NeuronCheckSystem::DeclarationMethods
588
-
589
- # extend済みフラグON
590
- @__neuron_check_extended = true
591
- end
592
- end
464
+ # チェック実行フラグ
465
+ @enabled = true
466
+ def self.enabled?; @enabled; end
467
+
468
+ # デバッグフラグ
469
+ @debug = false
470
+ def self.debug; @debug; end
471
+ def self.debug?; @debug; end
472
+ def self.debug=(v); @debug = v; end
473
+
474
+ # 無効化
475
+ def self.disable
476
+ if block_given? then
477
+ # ブロックが渡された場合は、ブロック実行中のみチェックを無効化
478
+ begin
479
+ disable
480
+ yield
481
+ ensure
482
+ enable
483
+ end
484
+ else
485
+ @enabled = false
486
+ NeuronCheckSystem::TRACE_POINT.disable # メソッド呼び出しフックを無効化
487
+ end
488
+ end
489
+
490
+ # 無効化されたチェックを、再度有効化
491
+ def self.enable
492
+ if block_given? then
493
+ # ブロックが渡された場合は、ブロック実行中のみチェックを有効か
494
+ begin
495
+ enable
496
+ yield
497
+ ensure
498
+ disable
499
+ end
500
+ else
501
+ @enabled = true
502
+ NeuronCheckSystem::TRACE_POINT.enable # メソッド呼び出しフックを有効化
503
+ end
504
+ end
505
+
506
+ # 単体チェック
507
+ def self.match?(value, &expected_block)
508
+ # 宣言ブロック実行用のコンテキストを作成
509
+ context = NeuronCheckSystem::DeclarationContext.new
510
+ # 宣言ブロックの内容を実行
511
+ expected = context.instance_eval(&expected_block)
512
+
513
+ matcher = NeuronCheckSystem.get_appropriate_matcher(expected, [])
514
+ return matcher.match?(value, nil)
515
+ end
516
+
517
+ # 単体チェック
518
+ def self.check(value, &expected_block)
519
+ # 宣言ブロック実行用のコンテキストを作成
520
+ context = NeuronCheckSystem::DeclarationContext.new
521
+ # 宣言ブロックの内容を実行
522
+ expected = context.instance_eval(&expected_block)
523
+
524
+ matcher = NeuronCheckSystem.get_appropriate_matcher(expected, [])
525
+ unless matcher.match?(value, nil) then
526
+ raise NeuronCheckError, matcher.get_error_message(nil, 'value', value), (NeuronCheck.debug? ? caller : caller(1))
527
+ end
528
+ end
529
+
530
+ # 宣言情報の出力
531
+ def self.get_declarations_as_json(ignore_unnamed_modules: false)
532
+ re = {'instance_methods' => {}, 'singleton_methods' => {}, 'attributes' => {}}
533
+
534
+ NeuronCheckSystem::METHOD_DECLARATIONS.each_pair do |cls_or_mod, data|
535
+ data.each_pair do |method_name, decl|
536
+ method_type = (decl.assinged_to_singleton_method? ? 'singleton_methods' : 'instance_methods')
537
+ target_mod_or_class = (decl.assinged_to_singleton_method? ? decl.assigned_singleton_original_class : decl.assigned_class_or_module)
538
+ key = target_mod_or_class.name
539
+
540
+ # 無名モジュール/クラスの場合の対応
541
+ if key.nil? and not ignore_unnamed_modules then
542
+ key = target_mod_or_class.inspect
543
+ end
544
+
545
+ if key then
546
+ re[method_type][key] ||= {}
547
+ re[method_type][key][method_name.to_s] = decl.meta_info_as_json
548
+ end
549
+ end
550
+ end
551
+
552
+ NeuronCheckSystem::ATTR_DECLARATIONS.each_pair do |cls_or_mod, data|
553
+ key = cls_or_mod.name
554
+
555
+ # 無名モジュール/クラスの場合の対応
556
+ if key.nil? and not ignore_unnamed_modules then
557
+ key = cls_or_mod.inspect
558
+ end
559
+
560
+ data.each_pair do |method_name, decl|
561
+ re['attributes'][key] ||= {}
562
+ re['attributes'][key][method_name.to_s] = decl.meta_info_as_json
563
+ end
564
+ end
565
+
566
+ re
567
+ end
568
+
569
+ # extend時処理
570
+ def self.extended(mod_or_class)
571
+ # extend対象がモジュールでもクラスでもなければエラー
572
+ unless mod_or_class.kind_of?(Module) then
573
+ raise ScriptError, "NeuronCheck can be extended only to Class or Module"
574
+ end
575
+
576
+ # 2回目以降のextendであれば何もせずにスルー
577
+ if mod_or_class.instance_variable_get(:@__neuron_check_extended) then
578
+ return
579
+ end
580
+
581
+ # まずはNeuronCheck用の初期化
582
+ NeuronCheckSystem.initialize_module_for_neuron_check(mod_or_class)
583
+
584
+ # 対象のModule/Classに対する処理
585
+ mod_or_class.instance_eval do
586
+ # Module/Classに対して宣言用のメソッドを追加する
587
+ extend NeuronCheckSystem::DeclarationMethods
588
+
589
+ # extend済みフラグON
590
+ @__neuron_check_extended = true
591
+ end
592
+ end
593
593
  end
594
594
 
595
595
 
596
596
 
597
597
  # トップレベル関数用の特殊なndecl
598
598
  self.instance_eval do
599
- def ndecl(*expecteds, &block)
600
- decl_caller = caller(2, 1)
601
-
602
- # Objectクラスへの宣言とみなす
603
- Object.class_eval do
604
- # extend NeuronCheckが実行されていない場合、NeuronCheck用の初期化を自動実行
605
- unless @__neuron_check_extended then
606
- extend NeuronCheck
607
- end
608
-
609
- # メイン処理実行
610
- __neuroncheck_ndecl_main(expecteds, block, decl_caller)
611
- end
612
- end
613
-
614
- alias ndeclare ndecl
615
- alias ncheck ndecl
616
- alias ntypesig ndecl
617
- alias nsig ndecl
599
+ def ndecl(*expecteds, &block)
600
+ decl_caller = caller(2, 1)
601
+
602
+ # Objectクラスへの宣言とみなす
603
+ Object.class_eval do
604
+ # extend NeuronCheckが実行されていない場合、NeuronCheck用の初期化を自動実行
605
+ unless @__neuron_check_extended then
606
+ extend NeuronCheck
607
+ end
608
+
609
+ # メイン処理実行
610
+ __neuroncheck_ndecl_main(expecteds, block, decl_caller)
611
+ end
612
+ end
613
+
614
+ alias ndeclare ndecl
615
+ alias ncheck ndecl
616
+ alias ntypesig ndecl
617
+ alias nsig ndecl
618
618
  end