neuroncheck 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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