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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 48bfd036b69c0f6d509363762c39a3fda0dc97f4
4
- data.tar.gz: 49471c3dd25296b6527572ec6fbf37aa6a896784
3
+ metadata.gz: d973bb5daf0142efc6ec453f3cec14766e4419f7
4
+ data.tar.gz: 55bfaa6ef9d5fe5bff49a6f9d5c51d907136c8cf
5
5
  SHA512:
6
- metadata.gz: 35c0916e4421893c8f1d175585f08586fd8e66f43d4b93a4ac10a99c65c4cee556c2c4ae49d030b1b23274eceab3a592b8e04182a857fc7405ec71a3b2495f5f
7
- data.tar.gz: 7f07bda2153ba07a262fae2afd1b3c6e6e078ff197aab19df8a9bb8445c54043cb3713676c380c4d553c33e997956603781a188c3547e53bab5f97c728175037
6
+ metadata.gz: 4b87e50fcfa401cd18f17f766248c305b1585d4c86effa564ac1fc4ec0b6a5261930fcefe37dd5a65fd5e06a9183ce939556bfb0e0af3c8ffa69aaffcb91cf88
7
+ data.tar.gz: e05cc9382ec1d88a8a5084cf5209a7758c3be5557b68c01a94f65abad6e200a124fc0281045028c554257de33c85c50d96933a7f768cb5b1978d4ca942349557
@@ -5,7 +5,7 @@ require 'neuroncheck/plugin'
5
5
  # respondable: 指定した名前のメソッドが定義されている(メソッド呼び出しに応答可能である)ことを表す。Duck Typing用
6
6
  NeuronCheckSystem::Plugin.add_keyword(:respondable) do
7
7
  def on_call(*method_names)
8
- @method_names = method_names
8
+ @method_names = method_names
9
9
  end
10
10
 
11
11
  def match?(value)
@@ -13,21 +13,21 @@ NeuronCheckSystem::Plugin.add_keyword(:respondable) do
13
13
  end
14
14
 
15
15
  def expected_caption
16
- in_cap = NeuronCheckSystem::Utils.string_join_using_or_conjunction(@method_names.map{|x| "##{x}"}) # 複数の文字列を結合し、orを使ったフレーズの形にするUtilityメソッド。 ['A', 'B', 'C'] => "A, B or C"
17
- "respondable to #{in_cap}"
16
+ in_cap = NeuronCheckSystem::Utils.string_join_using_or_conjunction(@method_names.map{|x| "##{x}"}) # 複数の文字列を結合し、orを使ったフレーズの形にするUtilityメソッド。 ['A', 'B', 'C'] => "A, B or C"
17
+ "respondable to #{in_cap}"
18
18
  end
19
19
 
20
- def expected_short_caption
20
+ def expected_short_caption
21
21
  'respondable(' + @method_names.map{|x| x.inspect}.join(', ') + ')'
22
22
  end
23
23
 
24
- def get_params_as_json
25
- {'expected' => @method_names.map(&:to_s)}
26
- end
24
+ def get_params_as_json
25
+ {'expected' => @method_names.map(&:to_s)}
26
+ end
27
27
 
28
- def self.builtin_keyword?
29
- true
30
- end
28
+ def self.builtin_keyword?
29
+ true
30
+ end
31
31
  end
32
32
 
33
33
  # respond_to, res: respondableのエイリアス
@@ -46,17 +46,17 @@ NeuronCheckSystem::Plugin.add_keyword(:any) do
46
46
  "any value"
47
47
  end
48
48
 
49
- def expected_short_caption
49
+ def expected_short_caption
50
50
  "any"
51
51
  end
52
52
 
53
- def get_params_as_json
54
- {}
55
- end
53
+ def get_params_as_json
54
+ {}
55
+ end
56
56
 
57
- def self.builtin_keyword?
58
- true
59
- end
57
+ def self.builtin_keyword?
58
+ true
59
+ end
60
60
  end
61
61
 
62
62
  # block: ブロック引数用の特殊なキーワード。[Proc, nil]と同じ
@@ -72,16 +72,16 @@ NeuronCheckSystem::Plugin.add_keyword(:block) do
72
72
  "block or nil"
73
73
  end
74
74
 
75
- def self.builtin_keyword?
76
- true
77
- end
75
+ def self.builtin_keyword?
76
+ true
77
+ end
78
78
  end
79
79
 
80
80
 
81
81
  # except: 指定した値以外を許可 (否定 / NOT)
82
82
  NeuronCheckSystem::Plugin.add_keyword(:except) do
83
83
  def on_call(target)
84
- @target = target
84
+ @target = target
85
85
  end
86
86
 
87
87
  def match?(value)
@@ -92,74 +92,74 @@ NeuronCheckSystem::Plugin.add_keyword(:except) do
92
92
  "any value except #{@api.get_expected_value_caption(@target)}"
93
93
  end
94
94
 
95
- def expected_short_caption
95
+ def expected_short_caption
96
96
  "except(#{@api.get_expected_value_short_caption(@target)})"
97
97
  end
98
98
 
99
- def get_params_as_json
100
- {'target' => @api.get_expected_value_meta_info_as_json(@target)}
101
- end
99
+ def get_params_as_json
100
+ {'target' => @api.get_expected_value_meta_info_as_json(@target)}
101
+ end
102
102
 
103
- def self.builtin_keyword?
104
- true
105
- end
103
+ def self.builtin_keyword?
104
+ true
105
+ end
106
106
  end
107
107
 
108
108
 
109
109
  # array_of: 指定した種類の値のみを格納した配列であることを表す
110
110
  NeuronCheckSystem::Plugin.add_keyword(:array_of) do
111
111
  def on_call(item_expected)
112
- @item_expected = item_expected
112
+ @item_expected = item_expected
113
113
  end
114
114
 
115
115
  def match?(value)
116
- return false unless value.kind_of?(Array) # まずは配列であるかどうかチェック
116
+ return false unless value.kind_of?(Array) # まずは配列であるかどうかチェック
117
117
 
118
- # 配列であれば、1つ1つの値が型どおりであるかどうかをチェック
119
- return value.all?{|x| @api.expected_value_match?(x, @item_expected)}
118
+ # 配列であれば、1つ1つの値が型どおりであるかどうかをチェック
119
+ return value.all?{|x| @api.expected_value_match?(x, @item_expected)}
120
120
  end
121
121
 
122
122
  def expected_caption
123
123
  "array of #{@api.get_expected_value_caption(@item_expected)}"
124
124
  end
125
125
 
126
- def get_params_as_json
127
- {'item' => @api.get_expected_value_meta_info_as_json(@item_expected)}
128
- end
126
+ def get_params_as_json
127
+ {'item' => @api.get_expected_value_meta_info_as_json(@item_expected)}
128
+ end
129
129
 
130
- def self.builtin_keyword?
131
- true
132
- end
130
+ def self.builtin_keyword?
131
+ true
132
+ end
133
133
  end
134
134
 
135
135
 
136
136
  # hash_of: 指定した種類のキーと値のみを格納した配列であることを表す
137
137
  NeuronCheckSystem::Plugin.add_keyword(:hash_of) do
138
- def on_call(key_expected, value_expected)
139
- @key_expected = key_expected
140
- @value_expected = value_expected
138
+ def on_call(key_expected, value_expected)
139
+ @key_expected = key_expected
140
+ @value_expected = value_expected
141
141
  end
142
142
 
143
- def match?(value)
144
- return false unless value.kind_of?(Hash) # まずはHashであるかどうかチェック
143
+ def match?(value)
144
+ return false unless value.kind_of?(Hash) # まずはHashであるかどうかチェック
145
145
 
146
- # ハッシュであれば、1つ1つのキーと値をチェック
147
- return value.all?{|k, v| @api.expected_value_match?(k, @key_expected) and @api.expected_value_match?(v, @value_expected)}
146
+ # ハッシュであれば、1つ1つのキーと値をチェック
147
+ return value.all?{|k, v| @api.expected_value_match?(k, @key_expected) and @api.expected_value_match?(v, @value_expected)}
148
148
  end
149
149
 
150
- def expected_caption
151
- "hash that has keys of #{@api.get_expected_value_caption(@key_expected)} and values of #{@api.get_expected_value_caption(@value_expected)}, #{expected_short_caption}"
150
+ def expected_caption
151
+ "hash that has keys of #{@api.get_expected_value_caption(@key_expected)} and values of #{@api.get_expected_value_caption(@value_expected)}, #{expected_short_caption}"
152
152
  end
153
153
 
154
- def expected_short_caption
155
- "{#{@api.get_expected_value_short_caption(@key_expected)} => #{api.get_expected_value_short_caption(@value_expected)}}"
154
+ def expected_short_caption
155
+ "{#{@api.get_expected_value_short_caption(@key_expected)} => #{api.get_expected_value_short_caption(@value_expected)}}"
156
156
  end
157
157
 
158
- def get_params_as_json
159
- {'key' => @api.get_expected_value_meta_info_as_json(@key_expected), 'value' => @api.get_expected_value_meta_info_as_json(@value_expected)}
160
- end
158
+ def get_params_as_json
159
+ {'key' => @api.get_expected_value_meta_info_as_json(@key_expected), 'value' => @api.get_expected_value_meta_info_as_json(@value_expected)}
160
+ end
161
161
 
162
- def self.builtin_keyword?
163
- true
164
- end
162
+ def self.builtin_keyword?
163
+ true
164
+ end
165
165
  end
@@ -1,42 +1,42 @@
1
1
  module NeuronCheckSystem
2
- class CondBlockContext
3
- def initialize(block_name, method_self, allow_instance_method)
4
- @block_name = block_name
5
- @method_self = method_self
6
- @allow_instance_method = allow_instance_method
7
- end
2
+ class CondBlockContext
3
+ def initialize(block_name, method_self, allow_instance_method)
4
+ @block_name = block_name
5
+ @method_self = method_self
6
+ @allow_instance_method = allow_instance_method
7
+ end
8
8
 
9
- def method_missing(name, *args, &block)
10
- if @method_self.respond_to?(name, true) then
11
- if @allow_instance_method then
12
- @method_self.send(name, *args, &block)
13
- else
14
- raise NeuronCheckSystem::DeclarationError, "instance method `#{name}' cannot be called in #{@block_name}, it is forbidden", (NeuronCheck.debug? ? caller : caller(1))
15
- end
16
- else
17
- super
18
- end
19
- end
9
+ def method_missing(name, *args, &block)
10
+ if @method_self.respond_to?(name, true) then
11
+ if @allow_instance_method then
12
+ @method_self.send(name, *args, &block)
13
+ else
14
+ raise NeuronCheckSystem::DeclarationError, "instance method `#{name}' cannot be called in #{@block_name}, it is forbidden", (NeuronCheck.debug? ? caller : caller(1))
15
+ end
16
+ else
17
+ super
18
+ end
19
+ end
20
20
 
21
- def assert(*dummy)
22
- unless block_given? then
23
- raise NeuronCheckSystem::DeclarationError, "no block given for `assert' in #{@block_name}", (NeuronCheck.debug? ? caller : caller(1))
24
- end
21
+ def assert(*dummy)
22
+ unless block_given? then
23
+ raise NeuronCheckSystem::DeclarationError, "no block given for `assert' in #{@block_name}", (NeuronCheck.debug? ? caller : caller(1))
24
+ end
25
25
 
26
- passed = yield
26
+ passed = yield
27
27
 
28
- unless passed
29
- locs = Utils.backtrace_locations_to_captions(caller(1, 1))
28
+ unless passed
29
+ locs = Utils.backtrace_locations_to_captions(caller(1, 1))
30
30
 
31
- msg = <<MSG
31
+ msg = <<MSG
32
32
  #{@block_name} assertion failed
33
33
  asserted at: #{locs.join("\n" + ' ' * 15)}
34
34
 
35
35
  MSG
36
36
 
37
- # エラーを発生させる
38
- throw :neuron_check_error_tag, msg
39
- end
40
- end
41
- end
37
+ # エラーを発生させる
38
+ throw :neuron_check_error_tag, msg
39
+ end
40
+ end
41
+ end
42
42
  end
@@ -4,292 +4,292 @@ require 'neuroncheck/syntax'
4
4
  require 'neuroncheck/builtin_keyword'
5
5
 
6
6
  module NeuronCheckSystem
7
- # 宣言用のメソッドやメソッド追加時の処理を定義したモジュール。NeuronCheckを行いたい対象のモジュールやクラスにextendすることで使用する
8
- module DeclarationMethods
9
- # 宣言を実行
10
- def ndecl(*expecteds, &block)
11
- # 未初期化の場合、NeuronCheck用の初期化を自動実行
12
- unless @__neuron_check_initialized then
13
- NeuronCheckSystem.initialize_module_for_neuron_check(self)
14
- end
15
-
16
- # メイン処理実行
17
- __neuroncheck_ndecl_main(expecteds, block, caller(1, 1))
18
- end
19
-
20
- # ndeclのエイリアス
21
- alias ncheck ndecl
22
- alias ndeclare ndecl
23
- alias nsig ndecl
24
- alias ntypesig ndecl
25
-
26
- # ndeclのメイン処理
27
- def __neuroncheck_ndecl_main(expecteds, block, declared_caller_locations)
28
- # 2回連続で宣言された場合はエラー
29
- if @__neuron_check_last_declaration then
30
- raise DeclarationError, "repeated declarations - Declaration block and method definition must correspond one-to-one"
31
- end
32
-
33
- # ブロックが渡されたかどうかで処理を分岐
34
- if block then
35
- # ブロックが渡された場合
36
- __neuroncheck_ndecl_main_with_block(block, declared_caller_locations)
37
- else
38
- # 短縮記法はNeuronCheckSyntax使用可能時のみ
39
- unless defined?(NeuronCheckSyntax) then
40
- raise DeclarationError, "NeuronCheck shorthand syntax (without block) can be used only in Ruby 2.1 or later"
41
- end
42
-
43
- # ブロックが渡されていない場合 (短縮記法)
44
- __neuroncheck_ndecl_main_without_block(expecteds, declared_caller_locations)
45
- end
46
- end
47
-
48
- # ndeclの通常記法
49
- def __neuroncheck_ndecl_main_with_block(block, declared_caller_locations)
50
- # 宣言ブロック実行用のコンテキストを作成
51
- context = NeuronCheckSystem::DeclarationContext.new
52
-
53
- # 宣言ブロックの内容を実行
54
- context.instance_eval(&block)
55
-
56
- # 呼び出し場所を記憶
57
- context.declaration.declared_caller_locations = declared_caller_locations
58
-
59
- # 宣言の内容を「最後の宣言」として保持
60
- @__neuron_check_last_declaration = context.declaration
61
- end
62
-
63
- # ndeclの短縮記法
64
- def __neuroncheck_ndecl_main_without_block(expecteds, declared_caller_locations)
65
- # 宣言ブロック実行用のコンテキストを作成
66
- context = NeuronCheckSystem::DeclarationContext.new
67
-
68
- # 引数の解釈
69
- expected_args = nil
70
- expected_return = nil
71
- if expecteds.last.kind_of?(Hash) and expecteds.last.size == 1 then
72
- # expectedsの最後が、値が1つだけ格納されたHashであれば、キーを最後の引数、値を戻り値と解釈する
73
- # 例: String, String => Numeric
74
- last_hash = expecteds.pop
75
- expected_args = expecteds.concat([last_hash.keys.first])
76
- expected_return = last_hash.values.first
77
- else
78
- # 上記以外の場合はすべて引数と見なす
79
- expected_args = expecteds
80
- end
81
-
82
- # 引数1つで、かつ空配列が渡された場合は、「引数なし」と宣言されたとみなす
83
- if expected_args[0].kind_of?(Array) and expected_args.size == 1 then
84
- expected_args = []
85
- end
86
-
87
- # 簡易宣言を実行
88
- context.instance_eval do
89
- unless expected_args.empty? then
90
- args *expected_args
91
- end
92
-
93
- if expected_return then
94
- returns expected_return
95
- end
96
- end
97
-
98
- # 短縮記法フラグON
99
- context.declaration.shorthand = true
100
-
101
- # 呼び出し場所を記憶
102
- context.declaration.declared_caller_locations = declared_caller_locations
103
- context.declaration.arg_matchers.each do |matcher|
104
- matcher.declared_caller_locations = context.declaration.declared_caller_locations
105
- end
106
- if context.declaration.return_matcher then
107
- context.declaration.return_matcher.declared_caller_locations = context.declaration.declared_caller_locations
108
- end
109
-
110
- # 宣言の内容を「最後の宣言」として保持 (通常のndeclと同じ)
111
- @__neuron_check_last_declaration = context.declaration
112
- end
113
- end
114
-
115
- class DeclarationContext
116
- include Keywords
117
- attr_reader :declaration
118
-
119
- def initialize
120
- @declaration = Declaration.new
121
- end
122
-
123
- def args(*expecteds)
124
- declared_caller_locations = caller(1, 1)
125
- @declaration.arg_matchers = expecteds.map{|x| NeuronCheckSystem.get_appropriate_matcher(x, declared_caller_locations)}
126
- end
127
-
128
- def returns(expected)
129
- declared_caller_locations = caller(1, 1)
130
- @declaration.return_matcher = NeuronCheckSystem.get_appropriate_matcher(expected, declared_caller_locations)
131
- end
132
-
133
- def precond(allow_instance_method: false, &cond_block)
134
- @declaration.precond = cond_block
135
- @declaration.precond_allow_instance_method = allow_instance_method
136
- end
137
-
138
- def postcond(allow_instance_method: false, &cond_block)
139
- @declaration.postcond = cond_block
140
- @declaration.postcond_allow_instance_method = allow_instance_method
141
- end
142
-
143
- def val(expected)
144
- declared_caller_locations = caller(1, 1)
145
- @declaration.attr_matcher = NeuronCheckSystem.get_appropriate_matcher(expected, declared_caller_locations)
146
- end
147
- alias must_be val
148
- alias value val
149
- end
150
-
151
- class Declaration
152
- attr_accessor :arg_matchers
153
- attr_accessor :return_matcher
154
- attr_accessor :attr_matcher
155
-
156
- attr_accessor :precond
157
- attr_accessor :precond_allow_instance_method
158
- attr_accessor :postcond
159
- attr_accessor :postcond_allow_instance_method
160
-
161
- attr_accessor :assigned_class_or_module
162
- attr_accessor :assigned_method
163
- attr_accessor :assigned_singleton_original_class
164
- attr_accessor :assigned_attribute_name
165
- attr_accessor :shorthand
166
- attr_accessor :declared_caller_locations
167
-
168
-
169
- def initialize
170
- @arg_matchers = []
171
- @return_matcher = nil
172
- @attr_matcher = nil
173
- @precond = nil
174
- @precond_allow_instance_method = false
175
- @postcond = nil
176
- @postcond_allow_instance_method = false
177
-
178
- @assigned_class_or_module = nil
179
- @assigned_method = nil
180
- @assigned_singleton_original_class = nil
181
- @assigned_attribute_name = nil
182
-
183
- @shorthand = false
184
- @declared_caller_locations = nil
185
- end
186
-
187
- def attribute?
188
- (@assigned_attribute_name ? true : false)
189
- end
190
-
191
- def assinged_to_toplevel_method?
192
- @assigned_class_or_module == Object
193
- end
194
- def assinged_to_singleton_method?
195
- @assigned_singleton_original_class
196
- end
197
-
198
- # メソッド名/属性名の表記文字列を取得
199
- def signature_caption_name_only
200
- if @assigned_class_or_module and (@assigned_method or attribute?) then
201
- ret = ""
202
-
203
- # 属性、特異メソッド、インスタンスメソッドのそれぞれで処理を分岐
204
- if attribute? then
205
- if @assigned_class_or_module.name then
206
- ret << @assigned_class_or_module.name
207
- end
208
-
209
- # 属性名出力
210
- ret << "##{@assigned_attribute_name}"
211
-
212
- elsif assinged_to_toplevel_method? then
213
- # メソッド名出力
214
- ret << "#{@assigned_method.name}"
215
-
216
- elsif assinged_to_singleton_method? then
217
- if @assigned_singleton_original_class.name then
218
- ret << @assigned_singleton_original_class.name
219
- end
220
-
221
- # メソッド名出力
222
- ret << ".#{@assigned_method.name}"
223
- else
224
- if @assigned_class_or_module.name then
225
- ret << @assigned_class_or_module.name
226
- end
227
-
228
- # メソッド名出力
229
- ret << "##{@assigned_method.name}"
230
- end
231
- else
232
- nil
233
- end
234
- end
235
-
236
- # メソッド名/属性名+引数+戻り値の表記文字列を取得
237
- def signature_caption
238
- ret = signature_caption_name_only
239
- if ret then
240
- if attribute? then
241
-
242
- if @attr_matcher then
243
- ret << " -> #{@attr_matcher.expected_short_caption}"
244
- end
245
- else
246
-
247
- # 引数出力
248
- unless @assigned_method.parameters.empty? then
249
- ret << "("
250
- @assigned_method.parameters.each_with_index do |param_info, i|
251
- _, param_name = param_info
252
- if i >= 1 then
253
- ret << ", "
254
- end
255
-
256
- if (matcher = @arg_matchers[i]) then
257
- ret << "#{param_name}:#{matcher.expected_short_caption}"
258
- else
259
- ret << "#{param_name}:any"
260
- end
261
- end
262
- ret << ")"
263
- end
264
-
265
- if @return_matcher then
266
- ret << " -> #{@return_matcher.expected_short_caption}"
267
- end
268
- end
269
-
270
- return ret
271
- else
272
- nil
273
- end
274
- end
275
-
276
- def meta_info_as_json
277
- re = {}
278
-
279
- if attribute? then
280
- re['value'] = (@attr_matcher ? @attr_matcher.meta_info_as_json : nil)
281
- re['signature_caption'] = signature_caption
282
- re['signature_caption_name_only'] = signature_caption_name_only
283
- else
284
- re['args'] = @arg_matchers.map{|x| x.meta_info_as_json}
285
- re['returns'] = (@return_matcher ? @return_matcher.meta_info_as_json : nil)
286
- re['signature_caption'] = signature_caption
287
- re['signature_caption_name_only'] = signature_caption_name_only
288
- end
289
- re['precond_source_location'] = (@precond ? @precond.source_location : nil)
290
- re['postcond_source_location'] = (@postcond ? @postcond.source_location : nil)
291
-
292
- re
293
- end
294
- end
7
+ # 宣言用のメソッドやメソッド追加時の処理を定義したモジュール。NeuronCheckを行いたい対象のモジュールやクラスにextendすることで使用する
8
+ module DeclarationMethods
9
+ # 宣言を実行
10
+ def ndecl(*expecteds, &block)
11
+ # 未初期化の場合、NeuronCheck用の初期化を自動実行
12
+ unless @__neuron_check_initialized then
13
+ NeuronCheckSystem.initialize_module_for_neuron_check(self)
14
+ end
15
+
16
+ # メイン処理実行
17
+ __neuroncheck_ndecl_main(expecteds, block, caller(1, 1))
18
+ end
19
+
20
+ # ndeclのエイリアス
21
+ alias ncheck ndecl
22
+ alias ndeclare ndecl
23
+ alias nsig ndecl
24
+ alias ntypesig ndecl
25
+
26
+ # ndeclのメイン処理
27
+ def __neuroncheck_ndecl_main(expecteds, block, declared_caller_locations)
28
+ # 2回連続で宣言された場合はエラー
29
+ if @__neuron_check_last_declaration then
30
+ raise DeclarationError, "repeated declarations - Declaration block and method definition must correspond one-to-one"
31
+ end
32
+
33
+ # ブロックが渡されたかどうかで処理を分岐
34
+ if block then
35
+ # ブロックが渡された場合
36
+ __neuroncheck_ndecl_main_with_block(block, declared_caller_locations)
37
+ else
38
+ # 短縮記法はNeuronCheckSyntax使用可能時のみ
39
+ unless defined?(NeuronCheckSyntax) then
40
+ raise DeclarationError, "NeuronCheck shorthand syntax (without block) can be used only in Ruby 2.1 or later"
41
+ end
42
+
43
+ # ブロックが渡されていない場合 (短縮記法)
44
+ __neuroncheck_ndecl_main_without_block(expecteds, declared_caller_locations)
45
+ end
46
+ end
47
+
48
+ # ndeclの通常記法
49
+ def __neuroncheck_ndecl_main_with_block(block, declared_caller_locations)
50
+ # 宣言ブロック実行用のコンテキストを作成
51
+ context = NeuronCheckSystem::DeclarationContext.new
52
+
53
+ # 宣言ブロックの内容を実行
54
+ context.instance_eval(&block)
55
+
56
+ # 呼び出し場所を記憶
57
+ context.declaration.declared_caller_locations = declared_caller_locations
58
+
59
+ # 宣言の内容を「最後の宣言」として保持
60
+ @__neuron_check_last_declaration = context.declaration
61
+ end
62
+
63
+ # ndeclの短縮記法
64
+ def __neuroncheck_ndecl_main_without_block(expecteds, declared_caller_locations)
65
+ # 宣言ブロック実行用のコンテキストを作成
66
+ context = NeuronCheckSystem::DeclarationContext.new
67
+
68
+ # 引数の解釈
69
+ expected_args = nil
70
+ expected_return = nil
71
+ if expecteds.last.kind_of?(Hash) and expecteds.last.size == 1 then
72
+ # expectedsの最後が、値が1つだけ格納されたHashであれば、キーを最後の引数、値を戻り値と解釈する
73
+ # 例: String, String => Numeric
74
+ last_hash = expecteds.pop
75
+ expected_args = expecteds.concat([last_hash.keys.first])
76
+ expected_return = last_hash.values.first
77
+ else
78
+ # 上記以外の場合はすべて引数と見なす
79
+ expected_args = expecteds
80
+ end
81
+
82
+ # 引数1つで、かつ空配列が渡された場合は、「引数なし」と宣言されたとみなす
83
+ if expected_args[0].kind_of?(Array) and expected_args.size == 1 then
84
+ expected_args = []
85
+ end
86
+
87
+ # 簡易宣言を実行
88
+ context.instance_eval do
89
+ unless expected_args.empty? then
90
+ args *expected_args
91
+ end
92
+
93
+ if expected_return then
94
+ returns expected_return
95
+ end
96
+ end
97
+
98
+ # 短縮記法フラグON
99
+ context.declaration.shorthand = true
100
+
101
+ # 呼び出し場所を記憶
102
+ context.declaration.declared_caller_locations = declared_caller_locations
103
+ context.declaration.arg_matchers.each do |matcher|
104
+ matcher.declared_caller_locations = context.declaration.declared_caller_locations
105
+ end
106
+ if context.declaration.return_matcher then
107
+ context.declaration.return_matcher.declared_caller_locations = context.declaration.declared_caller_locations
108
+ end
109
+
110
+ # 宣言の内容を「最後の宣言」として保持 (通常のndeclと同じ)
111
+ @__neuron_check_last_declaration = context.declaration
112
+ end
113
+ end
114
+
115
+ class DeclarationContext
116
+ include Keywords
117
+ attr_reader :declaration
118
+
119
+ def initialize
120
+ @declaration = Declaration.new
121
+ end
122
+
123
+ def args(*expecteds)
124
+ declared_caller_locations = caller(1, 1)
125
+ @declaration.arg_matchers = expecteds.map{|x| NeuronCheckSystem.get_appropriate_matcher(x, declared_caller_locations)}
126
+ end
127
+
128
+ def returns(expected)
129
+ declared_caller_locations = caller(1, 1)
130
+ @declaration.return_matcher = NeuronCheckSystem.get_appropriate_matcher(expected, declared_caller_locations)
131
+ end
132
+
133
+ def precond(allow_instance_method: false, &cond_block)
134
+ @declaration.precond = cond_block
135
+ @declaration.precond_allow_instance_method = allow_instance_method
136
+ end
137
+
138
+ def postcond(allow_instance_method: false, &cond_block)
139
+ @declaration.postcond = cond_block
140
+ @declaration.postcond_allow_instance_method = allow_instance_method
141
+ end
142
+
143
+ def val(expected)
144
+ declared_caller_locations = caller(1, 1)
145
+ @declaration.attr_matcher = NeuronCheckSystem.get_appropriate_matcher(expected, declared_caller_locations)
146
+ end
147
+ alias must_be val
148
+ alias value val
149
+ end
150
+
151
+ class Declaration
152
+ attr_accessor :arg_matchers
153
+ attr_accessor :return_matcher
154
+ attr_accessor :attr_matcher
155
+
156
+ attr_accessor :precond
157
+ attr_accessor :precond_allow_instance_method
158
+ attr_accessor :postcond
159
+ attr_accessor :postcond_allow_instance_method
160
+
161
+ attr_accessor :assigned_class_or_module
162
+ attr_accessor :assigned_method
163
+ attr_accessor :assigned_singleton_original_class
164
+ attr_accessor :assigned_attribute_name
165
+ attr_accessor :shorthand
166
+ attr_accessor :declared_caller_locations
167
+
168
+
169
+ def initialize
170
+ @arg_matchers = []
171
+ @return_matcher = nil
172
+ @attr_matcher = nil
173
+ @precond = nil
174
+ @precond_allow_instance_method = false
175
+ @postcond = nil
176
+ @postcond_allow_instance_method = false
177
+
178
+ @assigned_class_or_module = nil
179
+ @assigned_method = nil
180
+ @assigned_singleton_original_class = nil
181
+ @assigned_attribute_name = nil
182
+
183
+ @shorthand = false
184
+ @declared_caller_locations = nil
185
+ end
186
+
187
+ def attribute?
188
+ (@assigned_attribute_name ? true : false)
189
+ end
190
+
191
+ def assinged_to_toplevel_method?
192
+ @assigned_class_or_module == Object
193
+ end
194
+ def assinged_to_singleton_method?
195
+ @assigned_singleton_original_class
196
+ end
197
+
198
+ # メソッド名/属性名の表記文字列を取得
199
+ def signature_caption_name_only
200
+ if @assigned_class_or_module and (@assigned_method or attribute?) then
201
+ ret = ""
202
+
203
+ # 属性、特異メソッド、インスタンスメソッドのそれぞれで処理を分岐
204
+ if attribute? then
205
+ if @assigned_class_or_module.name then
206
+ ret << @assigned_class_or_module.name
207
+ end
208
+
209
+ # 属性名出力
210
+ ret << "##{@assigned_attribute_name}"
211
+
212
+ elsif assinged_to_toplevel_method? then
213
+ # メソッド名出力
214
+ ret << "#{@assigned_method.name}"
215
+
216
+ elsif assinged_to_singleton_method? then
217
+ if @assigned_singleton_original_class.name then
218
+ ret << @assigned_singleton_original_class.name
219
+ end
220
+
221
+ # メソッド名出力
222
+ ret << ".#{@assigned_method.name}"
223
+ else
224
+ if @assigned_class_or_module.name then
225
+ ret << @assigned_class_or_module.name
226
+ end
227
+
228
+ # メソッド名出力
229
+ ret << "##{@assigned_method.name}"
230
+ end
231
+ else
232
+ nil
233
+ end
234
+ end
235
+
236
+ # メソッド名/属性名+引数+戻り値の表記文字列を取得
237
+ def signature_caption
238
+ ret = signature_caption_name_only
239
+ if ret then
240
+ if attribute? then
241
+
242
+ if @attr_matcher then
243
+ ret << " -> #{@attr_matcher.expected_short_caption}"
244
+ end
245
+ else
246
+
247
+ # 引数出力
248
+ unless @assigned_method.parameters.empty? then
249
+ ret << "("
250
+ @assigned_method.parameters.each_with_index do |param_info, i|
251
+ _, param_name = param_info
252
+ if i >= 1 then
253
+ ret << ", "
254
+ end
255
+
256
+ if (matcher = @arg_matchers[i]) then
257
+ ret << "#{param_name}:#{matcher.expected_short_caption}"
258
+ else
259
+ ret << "#{param_name}:any"
260
+ end
261
+ end
262
+ ret << ")"
263
+ end
264
+
265
+ if @return_matcher then
266
+ ret << " -> #{@return_matcher.expected_short_caption}"
267
+ end
268
+ end
269
+
270
+ return ret
271
+ else
272
+ nil
273
+ end
274
+ end
275
+
276
+ def meta_info_as_json
277
+ re = {}
278
+
279
+ if attribute? then
280
+ re['value'] = (@attr_matcher ? @attr_matcher.meta_info_as_json : nil)
281
+ re['signature_caption'] = signature_caption
282
+ re['signature_caption_name_only'] = signature_caption_name_only
283
+ else
284
+ re['args'] = @arg_matchers.map{|x| x.meta_info_as_json}
285
+ re['returns'] = (@return_matcher ? @return_matcher.meta_info_as_json : nil)
286
+ re['signature_caption'] = signature_caption
287
+ re['signature_caption_name_only'] = signature_caption_name_only
288
+ end
289
+ re['precond_source_location'] = (@precond ? @precond.source_location : nil)
290
+ re['postcond_source_location'] = (@postcond ? @postcond.source_location : nil)
291
+
292
+ re
293
+ end
294
+ end
295
295
  end