leftovers 0.5.5 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +21 -1
  4. data/docs/Configuration.md +106 -2
  5. data/leftovers.gemspec +7 -4
  6. data/lib/config/ruby.yml +7 -0
  7. data/lib/leftovers/collector.rb +3 -1
  8. data/lib/leftovers/config.rb +8 -4
  9. data/lib/leftovers/config_validator/schema_hash.rb +170 -149
  10. data/lib/leftovers/config_validator.rb +1 -0
  11. data/lib/leftovers/definition.rb +1 -1
  12. data/lib/leftovers/dynamic_processors/call.rb +0 -4
  13. data/lib/leftovers/dynamic_processors/call_definition.rb +0 -4
  14. data/lib/leftovers/dynamic_processors/definition.rb +0 -4
  15. data/lib/leftovers/file.rb +52 -10
  16. data/lib/leftovers/file_collector.rb +33 -2
  17. data/lib/leftovers/json.rb +28 -0
  18. data/lib/leftovers/matcher_builders/document.rb +13 -0
  19. data/lib/leftovers/matcher_builders/node.rb +3 -1
  20. data/lib/leftovers/matcher_builders/node_has_argument.rb +3 -4
  21. data/lib/leftovers/matcher_builders/node_has_keyword_argument.rb +1 -1
  22. data/lib/leftovers/matcher_builders/node_has_positional_argument.rb +1 -1
  23. data/lib/leftovers/matcher_builders.rb +1 -0
  24. data/lib/leftovers/matchers/all.rb +0 -4
  25. data/lib/leftovers/matchers/and.rb +0 -4
  26. data/lib/leftovers/matchers/any.rb +0 -4
  27. data/lib/leftovers/matchers/node_has_any_keyword_argument.rb +1 -7
  28. data/lib/leftovers/matchers/node_has_any_positional_argument_with_value.rb +1 -7
  29. data/lib/leftovers/matchers/node_has_positional_argument_with_value.rb +0 -4
  30. data/lib/leftovers/matchers/node_name.rb +0 -4
  31. data/lib/leftovers/matchers/node_pair_value.rb +0 -4
  32. data/lib/leftovers/matchers/node_path.rb +0 -4
  33. data/lib/leftovers/matchers/node_scalar_value.rb +0 -4
  34. data/lib/leftovers/matchers/node_type.rb +0 -4
  35. data/lib/leftovers/matchers/not.rb +0 -4
  36. data/lib/leftovers/matchers/or.rb +0 -4
  37. data/lib/leftovers/merged_config.rb +19 -1
  38. data/lib/leftovers/processor_builders/dynamic.rb +2 -1
  39. data/lib/leftovers/todo_reporter.rb +10 -35
  40. data/lib/leftovers/value_processors/delete_prefix.rb +0 -6
  41. data/lib/leftovers/value_processors/delete_suffix.rb +0 -6
  42. data/lib/leftovers/value_processors/keyword.rb +0 -4
  43. data/lib/leftovers/value_processors/keyword_argument.rb +0 -4
  44. data/lib/leftovers/value_processors/return_definition.rb +0 -4
  45. data/lib/leftovers/version.rb +1 -1
  46. data/lib/leftovers/yaml.rb +73 -0
  47. data/lib/leftovers.rb +6 -7
  48. metadata +33 -16
  49. data/lib/leftovers/backports.rb +0 -40
@@ -44,19 +44,21 @@ module Leftovers
44
44
  'name' => {
45
45
  'anyOf' => [
46
46
  { '$ref' => '#/definitions/string' },
47
- { 'allOf' => [
48
- { '$ref' => '#/definitions/stringPattern' },
49
- {
50
- 'type' => 'object',
51
- 'properties' => {
52
- 'match' => true, 'matches' => true,
53
- 'has_prefix' => true, 'has_suffix' => true,
54
- 'unless' => { '$ref' => '#/definitions/nameList' }
55
- },
56
- 'minProperties' => 1,
57
- 'additionalProperties' => false
58
- }
59
- ] }
47
+ {
48
+ 'allOf' => [
49
+ { '$ref' => '#/definitions/stringPattern' },
50
+ {
51
+ 'type' => 'object',
52
+ 'properties' => {
53
+ 'match' => true, 'matches' => true,
54
+ 'has_prefix' => true, 'has_suffix' => true,
55
+ 'unless' => { '$ref' => '#/definitions/nameList' }
56
+ },
57
+ 'minProperties' => 1,
58
+ 'additionalProperties' => false
59
+ }
60
+ ]
61
+ }
60
62
  ]
61
63
  },
62
64
  'nameList' => {
@@ -107,40 +109,42 @@ module Leftovers
107
109
  { 'type' => 'number' },
108
110
  { 'type' => 'boolean' },
109
111
  { 'type' => 'null' },
110
- { 'allOf' => [
111
- { '$ref' => '#/definitions/stringPattern' },
112
- {
113
- 'type' => 'object',
114
- 'properties' => {
115
- 'match' => true, 'matches' => true,
116
- 'has_prefix' => true, 'has_suffix' => true,
117
- 'at' => { '$ref' => '#/definitions/argumentPositionList' },
118
- 'has_value' => { '$ref' => '#/definitions/hasValueList' },
119
- 'has_receiver' => { '$ref' => '#/definitions/hasValueList' },
120
- 'type' => { '$ref' => '#/definitions/valueTypeList' },
121
- 'unless' => { '$ref' => '#/definitions/hasValueList' }
122
- },
123
- 'minProperties' => 1,
124
- 'additionalProperties' => false,
125
- 'allOf' => [
126
- # incompatible groups
127
- { 'not' => { 'required' => %w{match at} } },
128
- { 'not' => { 'required' => %w{match has_value} } },
129
- { 'not' => { 'required' => %w{match type} } },
130
- { 'not' => { 'required' => %w{matches at} } },
131
- { 'not' => { 'required' => %w{matches has_value} } },
132
- { 'not' => { 'required' => %w{matches type} } },
133
- { 'not' => { 'required' => %w{has_prefix at} } },
134
- { 'not' => { 'required' => %w{has_prefix has_value} } },
135
- { 'not' => { 'required' => %w{has_prefix type} } },
136
- { 'not' => { 'required' => %w{has_suffix at} } },
137
- { 'not' => { 'required' => %w{has_suffix has_value} } },
138
- { 'not' => { 'required' => %w{has_suffix type} } },
139
- { 'not' => { 'required' => %w{at type} } },
140
- { 'not' => { 'required' => %w{has_value type} } }
141
- ]
142
- }
143
- ] }
112
+ {
113
+ 'allOf' => [
114
+ { '$ref' => '#/definitions/stringPattern' },
115
+ {
116
+ 'type' => 'object',
117
+ 'properties' => {
118
+ 'match' => true, 'matches' => true,
119
+ 'has_prefix' => true, 'has_suffix' => true,
120
+ 'at' => { '$ref' => '#/definitions/argumentPositionList' },
121
+ 'has_value' => { '$ref' => '#/definitions/hasValueList' },
122
+ 'has_receiver' => { '$ref' => '#/definitions/hasValueList' },
123
+ 'type' => { '$ref' => '#/definitions/valueTypeList' },
124
+ 'unless' => { '$ref' => '#/definitions/hasValueList' }
125
+ },
126
+ 'minProperties' => 1,
127
+ 'additionalProperties' => false,
128
+ 'allOf' => [
129
+ # incompatible groups
130
+ { 'not' => { 'required' => %w{match at} } },
131
+ { 'not' => { 'required' => %w{match has_value} } },
132
+ { 'not' => { 'required' => %w{match type} } },
133
+ { 'not' => { 'required' => %w{matches at} } },
134
+ { 'not' => { 'required' => %w{matches has_value} } },
135
+ { 'not' => { 'required' => %w{matches type} } },
136
+ { 'not' => { 'required' => %w{has_prefix at} } },
137
+ { 'not' => { 'required' => %w{has_prefix has_value} } },
138
+ { 'not' => { 'required' => %w{has_prefix type} } },
139
+ { 'not' => { 'required' => %w{has_suffix at} } },
140
+ { 'not' => { 'required' => %w{has_suffix has_value} } },
141
+ { 'not' => { 'required' => %w{has_suffix type} } },
142
+ { 'not' => { 'required' => %w{at type} } },
143
+ { 'not' => { 'required' => %w{has_value type} } }
144
+ ]
145
+ }
146
+ ]
147
+ }
144
148
  ]
145
149
  },
146
150
  'hasValueList' => {
@@ -187,6 +191,7 @@ module Leftovers
187
191
  'type' => 'object',
188
192
  'properties' => {
189
193
  'name' => { '$ref' => '#/definitions/nameList' },
194
+ 'document' => { '$ref' => '#/definitions/true' },
190
195
  'names' => { '$ref' => '#/definitions/nameList' },
191
196
  'path' => { '$ref' => '#/definitions/stringList' },
192
197
  'paths' => { '$ref' => '#/definitions/stringList' },
@@ -253,37 +258,39 @@ module Leftovers
253
258
  swapcase
254
259
  }
255
260
  },
256
- { 'allOf' => [
257
- { '$ref' => '#/definitions/transformProperties' },
258
- {
259
- 'type' => 'object',
260
- 'properties' => {
261
- 'original' => true,
262
- 'pluralize' => true,
263
- 'singularize' => true,
264
- 'camelize' => true,
265
- 'camelcase' => true,
266
- 'underscore' => true,
267
- 'titleize' => true,
268
- 'titlecase' => true,
269
- 'demodulize' => true,
270
- 'deconstantize' => true,
271
- 'parameterize' => true,
272
- 'downcase' => true,
273
- 'upcase' => true,
274
- 'capitalize' => true,
275
- 'swapcase' => true,
276
- 'add_prefix' => true,
277
- 'add_suffix' => true,
278
- 'split' => true,
279
- 'delete_prefix' => true,
280
- 'delete_suffix' => true,
281
- 'delete_before' => true,
282
- 'delete_after' => true
283
- },
284
- 'additionalProperties' => false
285
- }
286
- ] }
261
+ {
262
+ 'allOf' => [
263
+ { '$ref' => '#/definitions/transformProperties' },
264
+ {
265
+ 'type' => 'object',
266
+ 'properties' => {
267
+ 'original' => true,
268
+ 'pluralize' => true,
269
+ 'singularize' => true,
270
+ 'camelize' => true,
271
+ 'camelcase' => true,
272
+ 'underscore' => true,
273
+ 'titleize' => true,
274
+ 'titlecase' => true,
275
+ 'demodulize' => true,
276
+ 'deconstantize' => true,
277
+ 'parameterize' => true,
278
+ 'downcase' => true,
279
+ 'upcase' => true,
280
+ 'capitalize' => true,
281
+ 'swapcase' => true,
282
+ 'add_prefix' => true,
283
+ 'add_suffix' => true,
284
+ 'split' => true,
285
+ 'delete_prefix' => true,
286
+ 'delete_suffix' => true,
287
+ 'delete_before' => true,
288
+ 'delete_after' => true
289
+ },
290
+ 'additionalProperties' => false
291
+ }
292
+ ]
293
+ }
287
294
  ]
288
295
  },
289
296
  'transformList' => {
@@ -315,59 +322,63 @@ module Leftovers
315
322
  'anyOf' => [
316
323
  { '$ref' => '#/definitions/string' },
317
324
  { 'type' => 'integer', 'minimum' => 0 },
318
- { 'allOf' => [
319
- { '$ref' => '#/definitions/transformProperties' },
320
- {
321
- 'type' => 'object',
322
- 'properties' => {
323
- 'argument' => { '$ref' => '#/definitions/argumentPositionList' },
324
- 'arguments' => { '$ref' => '#/definitions/argumentPositionList' },
325
- 'keyword' => { '$ref' => '#/definitions/keywordList' },
326
- 'keywords' => { '$ref' => '#/definitions/keywordList' },
327
- 'itself' => { '$ref' => '#/definitions/true' },
328
- 'value' => { '$ref' => '#/definitions/string' },
329
- 'nested' => { '$ref' => '#/definitions/actionList' },
330
- 'recursive' => { '$ref' => '#/definitions/true' },
331
- 'transforms' => { '$ref' => '#/definitions/transformList' },
332
- 'pluralize' => true,
333
- 'singularize' => true,
334
- 'camelize' => true,
335
- 'camelcase' => true,
336
- 'underscore' => true,
337
- 'titleize' => true,
338
- 'titlecase' => true,
339
- 'demodulize' => true,
340
- 'deconstantize' => true,
341
- 'parameterize' => true,
342
- 'downcase' => true,
343
- 'upcase' => true,
344
- 'capitalize' => true,
345
- 'swapcase' => true,
346
- 'add_prefix' => true,
347
- 'add_suffix' => true,
348
- 'split' => true,
349
- 'delete_prefix' => true,
350
- 'delete_suffix' => true,
351
- 'delete_before' => true,
352
- 'delete_after' => true
353
- },
354
- 'additionalProperties' => false,
355
- 'allOf' => [
356
- # synonyms
357
- { 'not' => { 'required' => %w{keyword keywords} } },
358
- { 'not' => { 'required' => %w{argument arguments} } },
359
- # any of
360
- { 'anyOf' => [
361
- { 'required' => ['argument'] },
362
- { 'required' => ['arguments'] },
363
- { 'required' => ['keyword'] },
364
- { 'required' => ['keywords'] },
365
- { 'required' => ['itself'] },
366
- { 'required' => ['value'] }
367
- ] }
368
- ]
369
- }
370
- ] }
325
+ {
326
+ 'allOf' => [
327
+ { '$ref' => '#/definitions/transformProperties' },
328
+ {
329
+ 'type' => 'object',
330
+ 'properties' => {
331
+ 'argument' => { '$ref' => '#/definitions/argumentPositionList' },
332
+ 'arguments' => { '$ref' => '#/definitions/argumentPositionList' },
333
+ 'keyword' => { '$ref' => '#/definitions/keywordList' },
334
+ 'keywords' => { '$ref' => '#/definitions/keywordList' },
335
+ 'itself' => { '$ref' => '#/definitions/true' },
336
+ 'value' => { '$ref' => '#/definitions/string' },
337
+ 'nested' => { '$ref' => '#/definitions/actionList' },
338
+ 'recursive' => { '$ref' => '#/definitions/true' },
339
+ 'transforms' => { '$ref' => '#/definitions/transformList' },
340
+ 'pluralize' => true,
341
+ 'singularize' => true,
342
+ 'camelize' => true,
343
+ 'camelcase' => true,
344
+ 'underscore' => true,
345
+ 'titleize' => true,
346
+ 'titlecase' => true,
347
+ 'demodulize' => true,
348
+ 'deconstantize' => true,
349
+ 'parameterize' => true,
350
+ 'downcase' => true,
351
+ 'upcase' => true,
352
+ 'capitalize' => true,
353
+ 'swapcase' => true,
354
+ 'add_prefix' => true,
355
+ 'add_suffix' => true,
356
+ 'split' => true,
357
+ 'delete_prefix' => true,
358
+ 'delete_suffix' => true,
359
+ 'delete_before' => true,
360
+ 'delete_after' => true
361
+ },
362
+ 'additionalProperties' => false,
363
+ 'allOf' => [
364
+ # synonyms
365
+ { 'not' => { 'required' => %w{keyword keywords} } },
366
+ { 'not' => { 'required' => %w{argument arguments} } },
367
+ # any of
368
+ {
369
+ 'anyOf' => [
370
+ { 'required' => ['argument'] },
371
+ { 'required' => ['arguments'] },
372
+ { 'required' => ['keyword'] },
373
+ { 'required' => ['keywords'] },
374
+ { 'required' => ['itself'] },
375
+ { 'required' => ['value'] }
376
+ ]
377
+ }
378
+ ]
379
+ }
380
+ ]
381
+ }
371
382
  ]
372
383
  },
373
384
  'actionList' => {
@@ -395,10 +406,12 @@ module Leftovers
395
406
  { 'not' => { 'required' => %w{call calls} } },
396
407
  { 'not' => { 'required' => %w{define defines} } },
397
408
  # At least one of
398
- { 'anyOf' => [
399
- { 'required' => ['call'] }, { 'required' => ['calls'] },
400
- { 'required' => ['define'] }, { 'required' => ['defines'] }
401
- ] }
409
+ {
410
+ 'anyOf' => [
411
+ { 'required' => ['call'] }, { 'required' => ['calls'] },
412
+ { 'required' => ['define'] }, { 'required' => ['defines'] }
413
+ ]
414
+ }
402
415
  ]
403
416
  },
404
417
  'ruleMatcherList' => {
@@ -414,13 +427,15 @@ module Leftovers
414
427
  'ruleMatcher' => {
415
428
  'allOf' => [
416
429
  { '$ref' => '#/definitions/rulePattern' },
417
- { 'anyOf' => [
418
- { 'required' => ['name'] }, { 'required' => ['names'] },
419
- { 'required' => ['path'] }, { 'required' => ['paths'] },
420
- { 'required' => ['has_argument'] }, { 'required' => ['has_arguments'] },
421
- { 'required' => ['has_receiver'] },
422
- { 'required' => ['unless'] }
423
- ] },
430
+ {
431
+ 'anyOf' => [
432
+ { 'required' => ['name'] }, { 'required' => ['names'] },
433
+ { 'required' => ['path'] }, { 'required' => ['paths'] },
434
+ { 'required' => ['has_argument'] }, { 'required' => ['has_arguments'] },
435
+ { 'required' => ['has_receiver'] },
436
+ { 'required' => ['unless'] }
437
+ ]
438
+ },
424
439
  {
425
440
  'type' => 'object',
426
441
  'properties' => {
@@ -441,19 +456,23 @@ module Leftovers
441
456
  'allOf' => [
442
457
  { '$ref' => '#/definitions/rulePattern' },
443
458
  { '$ref' => '#/definitions/dynamicAction' },
444
- { 'anyOf' => [
445
- { 'required' => ['name'] }, { 'required' => ['names'] },
446
- { 'required' => ['path'] }, { 'required' => ['paths'] },
447
- { 'required' => ['has_argument'] }, { 'required' => ['has_arguments'] },
448
- { 'required' => ['has_receiver'] },
449
- { 'required' => ['unless'] }
450
- ] },
459
+ {
460
+ 'anyOf' => [
461
+ { 'required' => ['name'] }, { 'required' => ['names'] },
462
+ { 'required' => ['document'] },
463
+ { 'required' => ['path'] }, { 'required' => ['paths'] },
464
+ { 'required' => ['has_argument'] }, { 'required' => ['has_arguments'] },
465
+ { 'required' => ['has_receiver'] },
466
+ { 'required' => ['unless'] }
467
+ ]
468
+ },
451
469
  {
452
470
  'type' => 'object',
453
471
  'properties' => {
454
472
  # unfortunately this repetition is necessary to use additionalProperties: false
455
473
  'name' => true, 'names' => true,
456
474
  'path' => true, 'paths' => true,
475
+ 'document' => true,
457
476
  'has_argument' => true, 'has_arguments' => true,
458
477
  'has_receiver' => true,
459
478
  'unless' => { '$ref' => '#/definitions/ruleMatcherList' },
@@ -518,6 +537,8 @@ module Leftovers
518
537
  'test_paths' => { '$ref' => '#/definitions/stringList' },
519
538
  'haml_paths' => { '$ref' => '#/definitions/stringList' },
520
539
  'slim_paths' => { '$ref' => '#/definitions/stringList' },
540
+ 'yaml_paths' => { '$ref' => '#/definitions/stringList' },
541
+ 'json_paths' => { '$ref' => '#/definitions/stringList' },
521
542
  'erb_paths' => { '$ref' => '#/definitions/stringList' },
522
543
  'requires' => { '$ref' => '#/definitions/stringList' },
523
544
  'gems' => { '$ref' => '#/definitions/stringList' },
@@ -1,5 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
+ require 'set'
3
4
  require 'json_schemer'
4
5
 
5
6
  module Leftovers
@@ -10,7 +10,7 @@ module Leftovers
10
10
  def initialize(
11
11
  name,
12
12
  location: method_node.loc.expression,
13
- test: method_node.test_line?
13
+ test: method_node.test_line? || ::Leftovers.config.test_only === method_node
14
14
  )
15
15
  @name = name
16
16
  @path = location.source_buffer.name.to_s
@@ -3,10 +3,6 @@
3
3
  module Leftovers
4
4
  module DynamicProcessors
5
5
  class Call
6
- # :nocov:
7
- using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
- # :nocov:
9
-
10
6
  def initialize(matcher, processor)
11
7
  @matcher = matcher
12
8
  @processor = processor
@@ -3,10 +3,6 @@
3
3
  module Leftovers
4
4
  module DynamicProcessors
5
5
  class CallDefinition
6
- # :nocov:
7
- using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
- # :nocov:
9
-
10
6
  def initialize(matcher, call_processor, definition_processor)
11
7
  @matcher = matcher
12
8
  @call_processor = call_processor
@@ -3,10 +3,6 @@
3
3
  module Leftovers
4
4
  module DynamicProcessors
5
5
  class Definition
6
- # :nocov:
7
- using ::Leftovers::Backports::SetCaseEq if defined?(::Leftovers::Backports::SetCaseEq)
8
- # :nocov:
9
-
10
6
  def initialize(matcher, processor)
11
7
  @matcher = matcher
12
8
  @processor = processor
@@ -14,20 +14,62 @@ module Leftovers
14
14
  @test = Leftovers.config.test_paths.allowed?(relative_path)
15
15
  end
16
16
 
17
- def ruby
18
- precompiler&.precompile(read, self) || read
17
+ def ruby # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
18
+ precompiled = []
19
+ precompile = false
20
+
21
+ if haml?
22
+ precompiled << ::Leftovers::Haml.precompile(read, self)
23
+ precompile = true
24
+ end
25
+
26
+ if json?
27
+ precompiled << ::Leftovers::JSON.precompile(read, self)
28
+ precompile = true
29
+ end
30
+
31
+ if erb?
32
+ precompiled << ::Leftovers::ERB.precompile(read, self)
33
+ precompile = true
34
+ end
35
+
36
+ if slim?
37
+ precompiled << ::Leftovers::Slim.precompile(read, self)
38
+ precompile = true
39
+ end
40
+
41
+ if yaml?
42
+ precompiled << ::Leftovers::YAML.precompile(read, self)
43
+ precompile = true
44
+ end
45
+
46
+ if precompile
47
+ precompiled.join("\n")
48
+ else
49
+ read
50
+ end
19
51
  end
20
52
 
21
53
  private
22
54
 
23
- def precompiler
24
- if Leftovers.config.haml_paths.allowed?(relative_path)
25
- ::Leftovers::Haml
26
- elsif Leftovers.config.slim_paths.allowed?(relative_path)
27
- ::Leftovers::Slim
28
- elsif Leftovers.config.erb_paths.allowed?(relative_path)
29
- ::Leftovers::ERB
30
- end
55
+ def erb?
56
+ Leftovers.config.erb_paths.allowed?(relative_path)
57
+ end
58
+
59
+ def haml?
60
+ Leftovers.config.haml_paths.allowed?(relative_path)
61
+ end
62
+
63
+ def yaml?
64
+ Leftovers.config.yaml_paths.allowed?(relative_path)
65
+ end
66
+
67
+ def json?
68
+ Leftovers.config.json_paths.allowed?(relative_path)
69
+ end
70
+
71
+ def slim?
72
+ Leftovers.config.slim_paths.allowed?(relative_path)
31
73
  end
32
74
  end
33
75
  end
@@ -12,6 +12,7 @@ module Leftovers
12
12
  @definitions = []
13
13
  @allow_lines = Set.new.compare_by_identity
14
14
  @test_lines = Set.new.compare_by_identity
15
+ @dynamic_lines = {}
15
16
  @ruby = ruby
16
17
  @file = file
17
18
  end
@@ -58,14 +59,18 @@ module Leftovers
58
59
  LEFTOVERS_CALL_RE = /\bleftovers:call(?:s|ed|er|ers|) (#{NAME_RE}(?:[, :]+#{NAME_RE})*)/.freeze
59
60
  LEFTOVERS_ALLOW_RE = /\bleftovers:(?:keeps?|skip(?:s|ped|)|allow(?:s|ed|))\b/.freeze
60
61
  LEFTOVERS_TEST_RE = /\bleftovers:(?:for_tests?|tests?|testing|test_only)\b/.freeze
62
+ LEFTOVERS_DYNAMIC_RE = /\bleftovers:dynamic:(#{NAME_RE})\b/.freeze
63
+
61
64
  def process_comments(comments) # rubocop:disable Metrics/AbcSize
62
65
  comments.each do |comment|
63
66
  @allow_lines << comment.loc.line if comment.text.match?(LEFTOVERS_ALLOW_RE)
64
67
  @test_lines << comment.loc.line if comment.text.match?(LEFTOVERS_TEST_RE)
68
+ dynamic_match = comment.text.match(LEFTOVERS_DYNAMIC_RE)
69
+ @dynamic_lines[comment.loc.line] = dynamic_match[1] if dynamic_match
65
70
 
66
- next unless (match = comment.text.match(LEFTOVERS_CALL_RE))
71
+ next unless (call_match = comment.text.match(LEFTOVERS_CALL_RE))
67
72
 
68
- match[1].scan(NAME_RE).each { |s| add_call(s.to_sym) }
73
+ call_match[1].scan(NAME_RE).each { |s| add_call(s.to_sym) }
69
74
  end
70
75
  end
71
76
 
@@ -136,6 +141,16 @@ module Leftovers
136
141
  add_call(node.name)
137
142
  end
138
143
 
144
+ def on_array(node)
145
+ super
146
+ collect_commented_dynamic(node)
147
+ end
148
+
149
+ def on_hash(node)
150
+ super
151
+ collect_commented_dynamic(node)
152
+ end
153
+
139
154
  # grab e.g. :to_s in each(&:to_s)
140
155
  def on_block_pass(node)
141
156
  super
@@ -225,6 +240,22 @@ module Leftovers
225
240
  end
226
241
  end
227
242
 
243
+ def collect_commented_dynamic(node)
244
+ fake_method_name = @dynamic_lines[node.loc.line]
245
+ return unless fake_method_name
246
+
247
+ node = build_send_wrapper_for(node, fake_method_name)
248
+ collect_dynamic(node)
249
+ end
250
+
251
+ def build_send_wrapper_for(node, name)
252
+ ::Leftovers::AST::Node.new(
253
+ :send,
254
+ [nil, name.to_sym, *node.arguments],
255
+ location: node.location
256
+ )
257
+ end
258
+
228
259
  def collect_dynamic(node) # rubocop:disable Metrics/AbcSize
229
260
  node.keep_line = @allow_lines.include?(node.loc.line)
230
261
  node.test_line = test_line?(node.loc) unless node.keep_line?
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Leftovers
6
+ module JSON
7
+ class << self
8
+ def precompile(json, name)
9
+ "__leftovers_document(#{to_ruby_argument(::JSON.parse(json))})"
10
+ rescue ::JSON::ParserError => e
11
+ Leftovers.warn "#{e.class}: (#{name.relative_path}): #{e.message}"
12
+ ''
13
+ end
14
+
15
+ private
16
+
17
+ def to_ruby_argument(value)
18
+ ruby = value.inspect
19
+ return ruby unless value.is_a?(Array)
20
+
21
+ ruby.delete_prefix!('[')
22
+ ruby.delete_suffix!(']')
23
+
24
+ ruby
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Leftovers
4
+ module MatcherBuilders
5
+ module Document
6
+ def self.build(true_arg)
7
+ return unless true_arg
8
+
9
+ ::Leftovers::Matchers::NodeName.new(:__leftovers_document)
10
+ end
11
+ end
12
+ end
13
+ end