leftovers 0.2.3 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -0
  3. data/README.md +65 -49
  4. data/docs/Configuration.md +627 -0
  5. data/exe/leftovers +2 -2
  6. data/leftovers.gemspec +13 -6
  7. data/lib/config/attr_encrypted.yml +3 -4
  8. data/lib/config/audited.yml +9 -4
  9. data/lib/config/datagrid.yml +1 -1
  10. data/lib/config/flipper.yml +1 -3
  11. data/lib/config/graphql.yml +15 -13
  12. data/lib/config/okcomputer.yml +1 -3
  13. data/lib/config/parser.yml +89 -91
  14. data/lib/config/rails.yml +126 -107
  15. data/lib/config/redcarpet.yml +35 -38
  16. data/lib/config/rollbar.yml +1 -3
  17. data/lib/config/rspec.yml +18 -10
  18. data/lib/config/ruby.yml +42 -49
  19. data/lib/config/selenium-webdriver.yml +19 -0
  20. data/lib/config/sidekiq.yml +4 -8
  21. data/lib/config/will_paginate.yml +12 -14
  22. data/lib/leftovers.rb +66 -54
  23. data/lib/leftovers/ast.rb +8 -0
  24. data/lib/leftovers/ast/builder.rb +6 -3
  25. data/lib/leftovers/ast/node.rb +66 -107
  26. data/lib/leftovers/backports.rb +24 -40
  27. data/lib/leftovers/cli.rb +27 -15
  28. data/lib/leftovers/collector.rb +8 -11
  29. data/lib/leftovers/config.rb +38 -13
  30. data/lib/leftovers/config_validator.rb +60 -0
  31. data/lib/leftovers/config_validator/error_processor.rb +196 -0
  32. data/lib/leftovers/config_validator/schema_hash.rb +496 -0
  33. data/lib/leftovers/definition.rb +16 -41
  34. data/lib/leftovers/definition_node.rb +36 -0
  35. data/lib/leftovers/definition_set.rb +17 -24
  36. data/lib/leftovers/dynamic_processors.rb +11 -0
  37. data/lib/leftovers/dynamic_processors/call.rb +25 -0
  38. data/lib/leftovers/dynamic_processors/call_definition.rb +31 -0
  39. data/lib/leftovers/dynamic_processors/definition.rb +26 -0
  40. data/lib/leftovers/dynamic_processors/each.rb +19 -0
  41. data/lib/leftovers/dynamic_processors/null.rb +9 -0
  42. data/lib/leftovers/erb.rb +2 -2
  43. data/lib/leftovers/file.rb +3 -5
  44. data/lib/leftovers/file_collector.rb +79 -61
  45. data/lib/leftovers/file_list.rb +9 -15
  46. data/lib/leftovers/haml.rb +9 -12
  47. data/lib/leftovers/matcher_builders.rb +24 -0
  48. data/lib/leftovers/matcher_builders/and.rb +19 -0
  49. data/lib/leftovers/matcher_builders/and_not.rb +14 -0
  50. data/lib/leftovers/matcher_builders/argument_node_value.rb +21 -0
  51. data/lib/leftovers/matcher_builders/name.rb +29 -0
  52. data/lib/leftovers/matcher_builders/node.rb +40 -0
  53. data/lib/leftovers/matcher_builders/node_has_argument.rb +71 -0
  54. data/lib/leftovers/matcher_builders/node_has_keyword_argument.rb +22 -0
  55. data/lib/leftovers/matcher_builders/node_has_positional_argument.rb +24 -0
  56. data/lib/leftovers/matcher_builders/node_name.rb +15 -0
  57. data/lib/leftovers/matcher_builders/node_pair_name.rb +18 -0
  58. data/lib/leftovers/matcher_builders/node_pair_value.rb +16 -0
  59. data/lib/leftovers/matcher_builders/node_path.rb +14 -0
  60. data/lib/leftovers/matcher_builders/node_type.rb +28 -0
  61. data/lib/leftovers/matcher_builders/or.rb +73 -0
  62. data/lib/leftovers/matcher_builders/path.rb +15 -0
  63. data/lib/leftovers/matcher_builders/string.rb +11 -0
  64. data/lib/leftovers/matcher_builders/string_pattern.rb +19 -0
  65. data/lib/leftovers/matcher_builders/unless.rb +13 -0
  66. data/lib/leftovers/matchers.rb +26 -0
  67. data/lib/leftovers/matchers/all.rb +25 -0
  68. data/lib/leftovers/matchers/and.rb +24 -0
  69. data/lib/leftovers/matchers/any.rb +27 -0
  70. data/lib/leftovers/matchers/node_has_any_keyword_argument.rb +28 -0
  71. data/lib/leftovers/matchers/node_has_any_positional_argument_with_value.rb +25 -0
  72. data/lib/leftovers/matchers/node_has_positional_argument.rb +23 -0
  73. data/lib/leftovers/matchers/node_has_positional_argument_with_value.rb +25 -0
  74. data/lib/leftovers/matchers/node_name.rb +23 -0
  75. data/lib/leftovers/matchers/node_pair_value.rb +23 -0
  76. data/lib/leftovers/matchers/node_path.rb +23 -0
  77. data/lib/leftovers/matchers/node_scalar_value.rb +25 -0
  78. data/lib/leftovers/matchers/node_type.rb +23 -0
  79. data/lib/leftovers/matchers/not.rb +23 -0
  80. data/lib/leftovers/matchers/or.rb +26 -0
  81. data/lib/leftovers/merged_config.rb +29 -14
  82. data/lib/leftovers/parser.rb +1 -4
  83. data/lib/leftovers/processor_builders.rb +22 -0
  84. data/lib/leftovers/processor_builders/action.rb +63 -0
  85. data/lib/leftovers/processor_builders/add_prefix.rb +20 -0
  86. data/lib/leftovers/processor_builders/add_suffix.rb +20 -0
  87. data/lib/leftovers/processor_builders/argument.rb +25 -0
  88. data/lib/leftovers/processor_builders/dynamic.rb +27 -0
  89. data/lib/leftovers/processor_builders/each.rb +36 -0
  90. data/lib/leftovers/processor_builders/each_action.rb +51 -0
  91. data/lib/leftovers/processor_builders/each_dynamic.rb +54 -0
  92. data/lib/leftovers/processor_builders/each_for_definition_set.rb +36 -0
  93. data/lib/leftovers/processor_builders/itself.rb +13 -0
  94. data/lib/leftovers/processor_builders/keyword.rb +24 -0
  95. data/lib/leftovers/processor_builders/keyword_argument.rb +14 -0
  96. data/lib/leftovers/processor_builders/transform.rb +55 -0
  97. data/lib/leftovers/processor_builders/transform_chain.rb +24 -0
  98. data/lib/leftovers/processor_builders/transform_set.rb +47 -0
  99. data/lib/leftovers/processor_builders/value.rb +13 -0
  100. data/lib/leftovers/rake_task.rb +4 -4
  101. data/lib/leftovers/reporter.rb +56 -4
  102. data/lib/leftovers/todo_reporter.rb +124 -0
  103. data/lib/leftovers/value_processors.rb +40 -0
  104. data/lib/leftovers/value_processors/add_dynamic_prefix.rb +31 -0
  105. data/lib/leftovers/value_processors/add_dynamic_suffix.rb +31 -0
  106. data/lib/leftovers/value_processors/add_prefix.rb +20 -0
  107. data/lib/leftovers/value_processors/add_suffix.rb +20 -0
  108. data/lib/leftovers/value_processors/camelize.rb +24 -0
  109. data/lib/leftovers/value_processors/capitalize.rb +19 -0
  110. data/lib/leftovers/value_processors/deconstantize.rb +24 -0
  111. data/lib/leftovers/value_processors/delete_after.rb +22 -0
  112. data/lib/leftovers/value_processors/delete_before.rb +22 -0
  113. data/lib/leftovers/value_processors/delete_prefix.rb +26 -0
  114. data/lib/leftovers/value_processors/delete_suffix.rb +26 -0
  115. data/lib/leftovers/value_processors/demodulize.rb +24 -0
  116. data/lib/leftovers/value_processors/downcase.rb +19 -0
  117. data/lib/leftovers/value_processors/each.rb +21 -0
  118. data/lib/leftovers/value_processors/each_for_definition_set.rb +29 -0
  119. data/lib/leftovers/value_processors/each_keyword.rb +27 -0
  120. data/lib/leftovers/value_processors/each_keyword_argument.rb +27 -0
  121. data/lib/leftovers/value_processors/each_positional_argument.rb +24 -0
  122. data/lib/leftovers/value_processors/itself.rb +17 -0
  123. data/lib/leftovers/value_processors/keyword.rb +36 -0
  124. data/lib/leftovers/value_processors/keyword_argument.rb +36 -0
  125. data/lib/leftovers/value_processors/parameterize.rb +24 -0
  126. data/lib/leftovers/value_processors/placeholder.rb +18 -0
  127. data/lib/leftovers/value_processors/pluralize.rb +24 -0
  128. data/lib/leftovers/value_processors/positional_argument.rb +26 -0
  129. data/lib/leftovers/value_processors/replace_value.rb +18 -0
  130. data/lib/leftovers/value_processors/return_definition.rb +26 -0
  131. data/lib/leftovers/value_processors/return_string.rb +14 -0
  132. data/lib/leftovers/value_processors/singularize.rb +24 -0
  133. data/lib/leftovers/value_processors/split.rb +22 -0
  134. data/lib/leftovers/value_processors/swapcase.rb +19 -0
  135. data/lib/leftovers/value_processors/titleize.rb +24 -0
  136. data/lib/leftovers/value_processors/underscore.rb +24 -0
  137. data/lib/leftovers/value_processors/upcase.rb +19 -0
  138. data/lib/leftovers/version.rb +1 -1
  139. metadata +201 -25
  140. data/lib/config/selenium.yml +0 -21
  141. data/lib/leftovers/argument_rule.rb +0 -219
  142. data/lib/leftovers/core_ext.rb +0 -13
  143. data/lib/leftovers/hash_rule.rb +0 -40
  144. data/lib/leftovers/name_rule.rb +0 -53
  145. data/lib/leftovers/rule.rb +0 -74
  146. data/lib/leftovers/transform_rule.rb +0 -171
  147. data/lib/leftovers/value_rule.rb +0 -56
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 52698685d049f76cfbe3034d85b9de65aec3c06b44434119a6581b399778a648
4
- data.tar.gz: daa5a40d80c7a180eb81a4dd59bc841788da04db541cfc93f2ae02577b1fd251
3
+ metadata.gz: aa735d4aa3320b0225d0d256c296b5b3b284593f285d1257a4b8e6bd74a0dd74
4
+ data.tar.gz: ebaaa2f4c2e93780788e36d961a623a015b1f8d11c0a9799882e5a21a45552f4
5
5
  SHA512:
6
- metadata.gz: 98ab99b3d471cdf3d42cf2c2e4940fc985313d68ce6a1ffe7b2f2909b0bcd95c1019a5fccdb306177a8283c0837baf068942a9f390fc38ccdeed9f5ecf8019f7
7
- data.tar.gz: 1dcea67041a8afec49b80ff6b168d18fa28fcba7c6bbd546d0f004f76152f12116d9538cc0920e32721228a8a41233ad8788678d77fd55279f79b000e744ac55
6
+ metadata.gz: 88cf80851cdb71a94a3d5497fa80ab4b020447e6aa23b1a9074ef19789bcdc77b576275aad066ac23170b1d2d18687f0b07322b24792cd31a163f2e70f3d637e
7
+ data.tar.gz: '0167915df5ad01dcf2368e72a98171cad57b22e4df7d6c8e5bc29b1e8e053b8a9e522746836102d769ca80745105021bbeb297d038ab8cd58410a2d797c34d04'
data/CHANGELOG.md CHANGED
@@ -1,3 +1,42 @@
1
+ # v0.4.3
2
+ - add --write-todo so you can add this to your project without
3
+ immediately fixing everything.
4
+
5
+ # v0.4.2
6
+ - Make sorbet happy with this as a dependency
7
+ # v0.4.1
8
+ - add `test_only:` to mark methods/constants/assignments as test_only in the config rather than just with magic comments
9
+
10
+ # v0.4.0
11
+ - add `requires:` to .leftovers.yml config to e.g. load inflections in a different place than `config/initializers/inflections`
12
+ - REFACTORED EVERYTHING for a codebase i actually can extend
13
+ - Now with a very modified config api. After this i don't intend on every doing a rewrite like this again, so this is correcting all my bad decisions the first time through
14
+ - `rules.names` with `rules.skip: true` is now `keep.names` (see config/parser.yml)
15
+ - `rules.calls` and `rules.defines` is now `dynamic.calls` and `dynamic.defines`
16
+ - `rules.calls/defines.arguments.if` is now `keep/dynamic.calls/defines.has_arguments:` (see config/graphql.yml field)
17
+ - `rules.calls/defines.arguments.unless` is now `keep/dynamic.unless.has_arguments` (see config/graphql.yml field)
18
+ - `rules.calls/defines.transforms.replace_with` is now `keep/dynamic.calls/defines.value` (see custom_config_spec.rb html)
19
+ - `rules.calls/defines.transforms.add_prefix.from_argument/joiner:` is now `dynamic.calls/defines.transforms.add_prefix.argument/add_suffix` (see config/rails.yml delegate)
20
+ - `rules.calls/defines.transforms.add_suffix.from_argument/joiner:` is now `dynamic.calls/defines.transforms.add_suffix.argument/add_prefix`
21
+ - `rules.calls/defines.key: true` is now `dynamic.calls/defines.keywords: '**'`
22
+ - `rules.calls/defines.arguments.if.arguments.value` is now `keep/dynamic.has_arguments.has_value`
23
+ - `rules.calls/defines.arguments.if.arguments.value.type` is now `keep/dynamic.has_arguments.has_value_type`
24
+ - `rules.calls/defines.linked_transforms` is now `dynamic.calls/defines.transforms` (see config/rails.yml attribute). For the previous behaviour of the `transforms:` key simply add separate calls/defines entry. (see config/ruby.yml attr_accessor)
25
+ - `rules.calls/defines.keys: true` is now `dynamic.calls/defines.keywords: '**'` and can be given name patterns (e.g. unless:. see config/rails.yml validates)
26
+ - argument positions are now 0-indexed rather than 1-indexed
27
+ - no more automatic recursion, there's now two new keywords:
28
+ - `dynamic.calls/defines.arguments/keywords` with `dynamic.calls/defines.nested.arguments/keywords` will define the next layer of arguments to collect (see config/rails.yml validates)
29
+ - `dynamic.calls/defines.arguments/keywords` with `dynamic.calls/defines.recursive: true` will use the arguments & keywords layers recursively (see config/rails.yml permit)
30
+ - no more automatic splitting on ':' and '.', there's now a `dynamic.calls/defines.transforms.split: '::'` (see config/rails.yml resource)
31
+
32
+ # v0.3.0
33
+ - Add simplecov, fix a handful of bugs it found
34
+ - update fast_ignore
35
+
36
+ # v0.2.4
37
+ - use the right name for selenium-webdriver gem, so the bundler magic can work
38
+ - handle yaml syntax errors.
39
+
1
40
  # v0.2.3
2
41
  - restore ability to handle syntax errors. I really need to add coverage to this project
3
42
  - Fix bug with delete_after on an empty string
data/README.md CHANGED
@@ -1,16 +1,17 @@
1
1
  # Leftovers
2
- [![travis](https://travis-ci.org/robotdana/leftovers.svg?branch=master)](https://travis-ci.org/robotdana/leftovers)
2
+ [![travis](https://travis-ci.com/robotdana/leftovers.svg?branch=main)](https://travis-ci.com/robotdana/leftovers)
3
+ [![Gem Version](https://badge.fury.io/rb/leftovers.svg)](https://rubygems.org/gems/leftovers)
3
4
 
4
5
  Find unused `methods`, `Classes`, `CONSTANTS`, `@instance_variables`, `@@class_variables`, and `$global_variables` in your ruby projects.
5
6
 
6
7
  ## Why?
7
8
 
8
- Code that never gets executed is code that you shouldn't need to maintain.
9
+ Code that never gets executed is code that you shouldn't need to maintain
9
10
 
10
11
  - Leftovers from refactoring
11
12
  - Partially removed features
12
13
  - Typos and THIS NEVER WOULD HAVE WORKED code
13
- - Code that you only keep around because there are tests of it.
14
+ - Code that you only keep around because there are tests of it
14
15
 
15
16
  Leftovers will use static analysis to find these bits of code for you.
16
17
 
@@ -29,7 +30,7 @@ It's aware of how some gems call methods for you, including (still somewhat inco
29
30
  Add this line to your application's Gemfile:
30
31
 
31
32
  ```ruby
32
- gem 'leftovers'
33
+ gem 'leftovers', require: false
33
34
  ```
34
35
 
35
36
  And then execute:
@@ -42,7 +43,7 @@ Or install it yourself as:
42
43
 
43
44
  ## Usage
44
45
 
45
- Run `leftovers` in your terminal in the root of your project.
46
+ Run `leftovers` in your command line in the root of your project.
46
47
  This will output progress as it collects the calls/references and definitions in your project.
47
48
  Then it will output any defined methods (or classes etc) which are not called.
48
49
 
@@ -55,11 +56,42 @@ lib/hello_world.rb:18:6 another_tested_unused_method def another_tested_unused_m
55
56
  Not directly called at all:
56
57
  lib/hello_world.rb:6:6 generated_method= attr_accessor :generated_method
57
58
  lib/hello_world.rb:6:6 generated_method attr_accessor :generated_method
59
+
60
+ how to resolve: https://github.com/robotdana/leftovers/tree/main/Readme.md#how_to_resolve
58
61
  ```
59
62
 
63
+ if there is an overwhelming number of results, try using [`--write-todo`](#write-todo)
64
+
65
+ ## How to resolve
66
+
67
+ When running `leftovers` you'll be given a list of method, constant, and variable definitions it thinks are unused. Now what?
68
+
69
+ they were unintentionally left when removing their calls:
70
+ - remove their definitions. (they're still there in your git etc history if you want them back)
71
+
72
+ they are called dynamically:
73
+ - define how they're called dynamically in the [.leftovers.yml](#configuration-file); or
74
+ - mark the calls with [`# leftovers:call my_unused_method`](#leftovers-call); or
75
+ - mark the definition with [`# leftovers:keep`](#leftovers-keep)
76
+
77
+ they're defined intentionally to only be used by tests:
78
+ - add [`# leftovers:test_only`](#leftovers-test-only)
79
+
80
+ they're from a file that shouldn't be checked by leftovers:
81
+ - add the paths to the [`exclude_paths:`](https://github.com/robotdana/leftovers/tree/main/docs/Configuration.md#exclude_paths) list in the [.leftovers.yml](#configuration-file) file
82
+
83
+ if there are too many to address when first adding leftovers to your project, try running [`leftovers --write-todo`](#write-todo),
84
+
85
+ ### --write-todo
86
+
87
+ running `leftovers --write-todo` will generate a supplemental configuration file allowing all the currently detected uncalled definitions, which will be read on subsequent runs of `leftovers` without alerting any of the items mentioned in it.
88
+
89
+ commit this file so you/your team can gradually address these items while still having leftovers alert you to any newly unused items.
90
+
60
91
  ## Magic comments
61
92
 
62
93
  ### `# leftovers:keep`
94
+ _aliases `leftovers:keeps`, `leftovers:skip`, `leftovers:skips`, `leftovers:skipped`, `leftovers:allow`, `leftovers:allows`, `leftovers:allowed`_
63
95
  To mark a method definition as not unused, add the comment `# leftovers:keep` on the same line as the definition
64
96
 
65
97
  ```ruby
@@ -70,15 +102,16 @@ class MyClass
70
102
  end
71
103
  ```
72
104
  This would report `MyClass` is unused, but not my_method
73
- To do this for all definitions of this name, add the name with `skip: true` in the configuration file.
105
+ To do this for all definitions of this name, instead of adding a comment, add the name to the [`keep:`](https://github.com/robotdana/leftovers/tree/main/docs/Configuration.md#keep) list in the [configuration file](#configuration-file).
74
106
 
75
- ### `# leftovers:test`
107
+ ### `# leftovers:test_only`
108
+ _aliases `leftovers:for_test`, `leftovers:for_tests`, `leftovers:test`, `leftovers:tests`, `leftovers:testing`_
76
109
 
77
- To mark a definition from a non-test dir, as intentionally only used by tests, use `leftovers:test`
110
+ To mark a definition from a non-test dir, as intentionally only used by tests, use `leftovers:test_only`
78
111
  ```ruby
79
112
  # app/my_class.rb
80
113
  class MyClass
81
- def my_method # leftovers:test
114
+ def my_method # leftovers:test_only
82
115
  true
83
116
  end
84
117
  end
@@ -92,61 +125,44 @@ end
92
125
 
93
126
  This would consider `my_method` to be used, even though it is only called by tests.
94
127
 
128
+ To do this for all definitions of this name, instead of adding a comment, add the name to the [`test_only:`](https://github.com/robotdana/leftovers/tree/main/docs/Configuration.md#test_only) list in the [configuration file](#configuration-file).
129
+
95
130
  ### `# leftovers:call`
131
+ _aliases `leftovers:calls`_
96
132
  To mark a dynamic call that doesn't use literal values, use `leftovers:call` with the method name listed
97
133
  ```ruby
98
- method = [:puts, :warn].sample
99
- send(method, 'text') # leftovers:call puts, warn
134
+ method = [:puts, :warn].sample # leftovers:call puts, warn
135
+ send(method, 'text')
100
136
  ```
101
137
 
102
138
  This would consider `puts` and `warn` to both have been called
103
139
 
104
- ## Configuration
140
+ ## Configuration file
105
141
 
106
142
  The configuration is read from `.leftovers.yml` in your project root.
107
- Its presence is optional and all of these settings are optional:
108
-
109
- see the [complete config documentation](https://github.com/robotdana/leftovers/tree/master/Configuration.md) for details.
110
- see the [built in config files](https://github.com/robotdana/leftovers/tree/master/lib/config) for examples.
111
-
112
- - [`include_paths:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#include_paths)
113
- - [`exclude_paths:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#exclude_paths)
114
- - [`test_paths:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#test_paths)
115
- - [`gems:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#gems)
116
- - [`rules:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#rules)
117
- - [`names:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#names)
118
- - [`has_prefix:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#has_prefix-has_suffix)
119
- - [`has_suffix:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#has_prefix-has_suffix)
120
- - [`matches:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#matches)
121
- - [`paths:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#paths)
122
- - [`skip:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#skip)
123
- - [`calls:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#calls-defines), [`defines:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#calls-defines)
124
- - [`arguments:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#arguments), [`keys:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#keys-), [`itself:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#itself-true)
125
- - [`transforms:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#transforms), [`linked_transforms:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#linked_transforms)
126
- - `original:`, `add_prefix:`, `add_suffix:`, `delete_prefix:`, `delete_suffix:`, `replace_with:`
127
- - `delete_before:`, `delete_after:`, `downcase:`, `upcase:`, `capitalize:`, `swapcase:`
128
- - `pluralize`, `singularize`, `camelize`, `underscore`, `demodulize`, `deconstantize`
129
- - [`if:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#if-unless), [`unless:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#if-unless)
130
- - [`has_argument:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#has_argument)
131
- - `keyword:`
132
- - [`has_prefix:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#has_prefix-has_suffix)
133
- - [`has_suffix:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#has_prefix-has_suffix)
134
- - [`matches:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#matches)
135
- - `value:`
136
- - [`has_prefix:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#has_prefix-has_suffix)
137
- - [`has_suffix:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#has_prefix-has_suffix)
138
- - [`matches:`](https://github.com/robotdana/leftovers/tree/master/Configuration.md#matches)
139
- - `type:`
143
+ Its presence is optional and all of these settings are optional.
144
+
145
+ - [`include_paths:`](https://github.com/robotdana/leftovers/tree/main/docs/Configuration.md#include_paths)
146
+ - [`exclude_paths:`](https://github.com/robotdana/leftovers/tree/main/docs/Configuration.md#exclude_paths)
147
+ - [`test_paths:`](https://github.com/robotdana/leftovers/tree/main/docs/Configuration.md#test_paths)
148
+ - [`requires:`](https://github.com/robotdana/leftovers/tree/main/docs/Configuration.md#requires)
149
+ - [`gems:`](https://github.com/robotdana/leftovers/tree/main/docs/Configuration.md#gems)
150
+ - [`keep:`](https://github.com/robotdana/leftovers/tree/main/docs/Configuration.md#keep)
151
+ - [`test_only:`](https://github.com/robotdana/leftovers/tree/main/docs/Configuration.md#test_only)
152
+ - [`dynamic:`](https://github.com/robotdana/leftovers/tree/main/docs/Configuration.md#dynamic)
153
+
154
+ see the [complete config documentation](https://github.com/robotdana/leftovers/tree/main/docs/Configuration.md) for details.
155
+ see the [built in config files](https://github.com/robotdana/leftovers/tree/main/lib/config) or [this repo's own config](https://github.com/robotdana/leftovers/tree/main/.leftovers.yml) for examples.
140
156
 
141
157
  ## Limitations
142
158
 
143
159
  - Leftovers will report methods/constants you define that are called outside your code (perhaps by gems) as unused
144
160
 
145
- Add these names to the `rules:` list with `skip: true` in the `.leftovers.yml` or add an inline comment with `# leftovers:allow my_method_name`
146
- - Leftovers doesn't execute your code so isn't aware of dynamic calls to `send` (e.g. `send(variable_method_name)`). (it is aware of static calls (e.g. `send(:my_method_name)`), so using send to bypass method privacy is "fine")
161
+ Add these names to the `keep:` list in the `.leftovers.yml` or add an inline comment with `# leftovers:allow my_method_name`
162
+ - Leftovers doesn't execute your code so isn't aware of e.g. variables in calls to `send` (e.g. `send(variable_method_name)`). (it is aware of static calls (e.g. `send(:my_method_name)`), so using send to bypass method privacy is "fine")
147
163
 
148
- Add the method/pattern to the `rules:` list with `skip: true` in the `.leftovers.yml`, or add an inline comment with the list of possibilities `# leftovers:call my_method_1, my_method_2`.
149
- - Leftovers compares by name only, so multiple methods with the same name will count as used even if only one is.
164
+ Add the method/pattern to the `dynamic:` list with `skip: true` in the `.leftovers.yml`, or add an inline comment with the list of possibilities `# leftovers:call my_method_1, my_method_2`.
165
+ - Leftovers compares by name only, so multiple definitions with the same name will count as used even if only one is.
150
166
  - haml & erb line and column numbers will be wrong as the files have to be precompiled before checking.
151
167
 
152
168
  ## Other tools
@@ -0,0 +1,627 @@
1
+ # Configuration
2
+
3
+ The configuration is read from `.leftovers.yml` in your project root.
4
+ Its presence is optional and all of these settings are optional.
5
+
6
+ - [`include_paths:`](#include_paths)
7
+ - [`exclude_paths:`](#exclude_paths)
8
+ - [`test_paths:`](#test_paths)
9
+ - [`requires:`](#requires)
10
+ - [`gems:`](#gems)
11
+ - [`keep:`](#keep)
12
+ - [`test_only:](#test_only)
13
+ - [`dynamic:`](#dynamic)
14
+
15
+ see the [built in config files](https://github.com/robotdana/leftovers/tree/main/lib/config) or [this repo's own config](https://github.com/robotdana/leftovers/tree/main/.leftovers.yml) for examples.
16
+
17
+ ## `include_paths:`
18
+ _alias `include_path:`_
19
+
20
+ List filenames/paths in the gitignore format of files to be checked
21
+ Defined using the [.gitignore pattern format](https://git-scm.com/docs/gitignore#_pattern_format)
22
+
23
+ By default leftovers will already check all `*.rb` files, and files with no extension and a shebang containing `ruby` e.g. (`#!/usr/bin/env ruby`)
24
+ Also the config added in [`gems:`](#gems) may add to this list, e.g. `gems: rake` adds `Rakefile` and `*.rake` to be checked.
25
+
26
+ ```yml
27
+ include_paths:
28
+ - '*.rb'
29
+ - '*.rake'
30
+ - Gemfile
31
+ ```
32
+
33
+ Arrays are not necessary for single values
34
+
35
+ ## `exclude_paths:`
36
+ _alias `exclude_path:`_
37
+
38
+ List filenames/paths that match the above that you might want to exclude
39
+ Defined using the [.gitignore pattern format](https://git-scm.com/docs/gitignore#_pattern_format)
40
+
41
+ By default leftovers will already read your project's .gitignore file and ignore anything there.
42
+
43
+ ```yml
44
+ exclude_paths:
45
+ - /some/long/irrelevant/generated/file
46
+ ```
47
+
48
+ Arrays are not necessary for single values
49
+
50
+ ## `requires:` # TODO
51
+ _alias `require`_
52
+
53
+ List filenames/paths that you want to include
54
+ Unlike other `paths:` configuration, each entry is **not** the gitignore pattern format.
55
+ Instead it is strings that can be passed directly to ruby's `require` method (relative paths should start with `./`).
56
+
57
+ Missing files/gems will be a warning, but not a LoadError.
58
+
59
+ By default, the default [`gems: active_support`]() config `require: activesupport` and `gems: rails` will `require: ./config/initializers/inflections`
60
+
61
+ ```yml
62
+ require: ./config/initializers/my_other_inflections_file
63
+ ```
64
+
65
+ Arrays are not necessary for single values
66
+
67
+ ## `test_paths:`
68
+ _alias `test_path:`_
69
+
70
+ list filenames/paths of test directories that will be used to determine if a method/etc is only tested but not otherwise used.
71
+ Defined using the [.gitignore pattern format](https://git-scm.com/docs/gitignore#_pattern_format)
72
+
73
+ ```yml
74
+ test_paths:
75
+ - /test/
76
+ - /spec/
77
+ ```
78
+
79
+ Arrays are not necessary for single values
80
+
81
+ ## `gems:`
82
+ _alias `gem:`_
83
+
84
+ By default Leftovers will look at your `Gemfile.lock` file to find all gem dependencies
85
+
86
+ If you don't use bundler, you can still take advantage of the built in handling for certain gems. The arguments should exactly match the gems name. not all gems are supported yet.
87
+
88
+ ```yml
89
+ gems:
90
+ - rspec
91
+ - rails
92
+ ```
93
+
94
+ Arrays are not necessary for single values
95
+
96
+ ## `keep:`
97
+
98
+ This is a list of methods/constants/variables that are ok to be defined and not used, because they're your public api, or called by a gem on your behalf or etc.
99
+
100
+ Each entry can be a string (an exact match for a method, constant, or variable name that includes the sigil), or have at least one of the following properties:
101
+ - [`names:`](#names)
102
+ or the properties from `names:`
103
+ - [`has_prefix:`](#has_prefix)
104
+ - [`has_suffix:`](#has_suffix)
105
+ - [`matches:`](#matches) (can't be used in the same entry as `has_prefix:` or `has_suffix:`)
106
+ - [`paths:`](#paths)
107
+ - [`has_arguments:`](#has_arguments)
108
+ - [`unless`](#unless)
109
+
110
+ Arrays are not necessary for single values
111
+
112
+ example from rails.yml
113
+ ```yml
114
+ keep:
115
+ - APP_PATH
116
+ - ssl_configured?
117
+ - has_suffix: Helper
118
+ path: /app/helpers
119
+ ...
120
+ ```
121
+
122
+ Alternatively, you can mark method/constants/variables in-place using [magic comments](https://github.com/robotdana/leftovers/tree/main/README.md#magic-comments).
123
+
124
+ ## `test_only:`
125
+
126
+ This is a list of methods/constants/variables that are ok to be defined outside of [test paths](#test_paths), but only used within test paths, maybe because they're your public api, or convenience methods for tests etc.
127
+
128
+ Each entry can be a string (an exact match for a method, constant, or variable name that includes the sigil), or have at least one of the following properties:
129
+ - [`names:`](#names)
130
+ or the properties from `names:`
131
+ - [`has_prefix:`](#has_prefix)
132
+ - [`has_suffix:`](#has_suffix)
133
+ - [`matches:`](#matches) (can't be used in the same entry as `has_prefix:` or `has_suffix:`)
134
+ - [`paths:`](#paths)
135
+ - [`has_arguments:`](#has_arguments)
136
+ - [`unless`](#unless)
137
+
138
+ Arrays are not necessary for single values
139
+
140
+ example from rails.yml
141
+ ```yml
142
+ test_only:
143
+ - APP_PATH
144
+ - ssl_configured?
145
+ - has_suffix: Helper
146
+ path: /app/helpers
147
+ ...
148
+ ```
149
+
150
+ Alternatively, you can mark method/constants/variables in-place using [magic comments](https://github.com/robotdana/leftovers/tree/main/README.md#magic-comments).
151
+
152
+ ## `dynamic:`
153
+
154
+ This is a list of methods, constants, or variables whose called arguments or assigned value/s are used to dynamically `call:` or define (`define:`) other methods, constants, or variables
155
+
156
+ Each entry must have at least one of the following properties to restrict which method/constant/variable this applies to:
157
+ - [`names:`](#names)
158
+ or the properties from `names:`
159
+ - [`has_prefix:`](#has_prefix)
160
+ - [`has_suffix:`](#has_suffix)
161
+ - [`matches:`](#matches)
162
+ - [`paths:`](#paths)
163
+ - [`has_arguments:`](#has_arguments)
164
+ - [`unless`](#unless)
165
+
166
+ And must have one or both of
167
+ - ['calls:`](#calls-defines)
168
+ - [`defines:`](#calls-defines)
169
+
170
+ Arrays are not necessary for single values.
171
+
172
+ example from the default ruby.yml
173
+ ```yml
174
+ dynamic:
175
+ - name: attr_accessor
176
+ defines:
177
+ - arguments: '*'
178
+ add_suffix: '='
179
+ - arguments: '*'
180
+ calls:
181
+ arguments: '*'
182
+ add_prefix: '@'
183
+ ...
184
+ ```
185
+
186
+ ## `names:`
187
+ _alias `name:`_
188
+
189
+ This is a list of methods/constants/variables, and can be used in [`dynamic:`](#dynamic) and [`keep:`](#keep)
190
+
191
+ Each entry can be a string (an exact match for a method, constant, or variable name that includes the sigil), or have at least one of the following properties:
192
+ - [`has_prefix:`](#has_prefix)
193
+ - [`has_suffix:`](#has_suffix)
194
+ - [`matches:`](#matches) (this can't be used in the same entry as `has_prefix:` or `has_suffix:`)
195
+
196
+ Arrays are not necessary for single values
197
+
198
+ example from rails.yml
199
+ ```yml
200
+ keep:
201
+ - APP_PATH
202
+ - ssl_configured?
203
+ - names:
204
+ has_suffix: Helper
205
+ path: /app/helpers
206
+ ...
207
+ ```
208
+
209
+ ## `has_prefix:`, `has_suffix:`
210
+
211
+ To match strings other than exact strings, you can use has_suffix or has_prefix or both if you're feeling fancy.
212
+
213
+ This can be in entries in:
214
+ - [`dynamic:`](#dynamic), [`keep:`](#keep), [`names:`](#names) to match method/constant/variable names
215
+ - [`has_arguments:`](#has_arguments), [`arguments:`](#arguments), [`keywords:`](#keywords), [`at:`](#at) to match keyword argument names (whether they're symbols or strings)
216
+ - [`has_values:`](#has_values) to match argument/assigned values (whether they're symbols or strings)
217
+
218
+ ```yml
219
+ keep:
220
+ - has_suffix: Helper # will match Helper, LinkHelper FormHelper, etc
221
+ - has_prefix: be_ # will match be_equal, be_invalid, etc
222
+ - { has_prefix: is_, has_suffix: '?' } # will match is_invalid?, is_equal? etc
223
+ ```
224
+
225
+ ## `matches:`
226
+ _alias `match:`_
227
+
228
+ if [`has_suffix:`](#has_prefix-has_suffix) and [`has_prefix:`](#has_prefix-has_suffix) isn't enough, you can use `matches:` to supply a regex
229
+
230
+ This string is converted into an anchored ruby regexp and must match the whole string, see [ruby's regex documentation](https://ruby-doc.org/core-2.7.2/Regexp.html#class-Regexp-label-Metacharacters+and+Escapes) for the syntax.
231
+
232
+ This can be in used for entries in:
233
+ - [`dynamic:`](#dynamic), [`keep:`](#keep), [`names:`](#names) to match method/constant/variable names
234
+ - [`has_arguments:`](#has_arguments), [`arguments:`](#arguments), [`keywords:`](#keywords) to match keyword argument names (whether they're symbols or strings in the code)
235
+
236
+ ```yml
237
+ dynamic:
238
+ - names:
239
+ matches: 'row_\d+' # will match row_1, row_2, row_99, but not row_1a, or crow_1, or etc.
240
+ calls:
241
+ argument:
242
+ matches: 'column_\d+' # will match column_1, column_2, column_99, but not column_first etc
243
+ ```
244
+
245
+ ## `paths:`
246
+ _alias `path:`_
247
+
248
+ An list of paths, defined using the [.gitignore pattern format](https://git-scm.com/docs/gitignore#_pattern_format)
249
+
250
+ This can be used in entries in [`dynamic:`](#dynamic), [`keep:`](#keep)
251
+
252
+ Arrays are not necessary for single values
253
+
254
+ ```yml
255
+ keep:
256
+ - name:
257
+ - has_suffix: Helper
258
+ path: /app/helpers
259
+ ```
260
+
261
+ ## `calls:`, `defines:`
262
+ _aliases `call:`, `define:`_
263
+
264
+ At least one of these must be used in each entry in [`dynamic:`](#dynamic)
265
+
266
+ This is a list of values that are called or defined dynamically by the matched method, or eventually after being assigned to the the matched constant or variable.
267
+
268
+ Each entry must be have at least one the these properties:
269
+ - [`arguments:`](#arguments)
270
+ - [`keywords:`](#keywords) keyword argument keywords, and assigned hash keys
271
+ - [`itself: true`](#itself-true) the matched name (intended to be used with `transforms:`, as the untransformed matched name will already be tracked)
272
+ - [`value:`](#value) a literal string to be called/defined
273
+
274
+ also there may be any or all of these properties:
275
+ - [`nested:`](#nested) (only with `arguments:`)
276
+ - [`recursive: true`](#recursive-true) (only with `arguments:`)
277
+ - [`transforms:`](#transforms)
278
+ - or any these properties for `transform:`
279
+ - [`add_prefix:`](#add_prefix-add_suffix)
280
+ - [`add_suffix:`](#add_prefix-add_suffix)
281
+ - [`delete_prefix:`](#delete_prefix-delete_suffix)
282
+ - [`delete_suffix:`](#delete_prefix-delete_suffix)
283
+ - [`delete_before:`](#delete_before-delete_after)
284
+ - [`delete_after:`](#delete_before-delete_after)
285
+ - [`split:`](#split)
286
+ - [`downcase:`](#downcase-upcase-capitalize-swapcase)
287
+ - [`upcase:`](#downcase-upcase-capitalize-swapcase)
288
+ - [`capitalize:`](#downcase-upcase-capitalize-swapcase)
289
+ - [`swapcase:`](#downcase-upcase-capitalize-swapcase)
290
+ - [`pluralize:`](#pluralize-singularize-camelize-demodulize-deconstantize-parameterize-titleize-underscore)
291
+ - [`singularize:`](#pluralize-singularize-camelize-demodulize-deconstantize-parameterize-titleize-underscore)
292
+ - [`camelize:`](#pluralize-singularize-camelize-demodulize-deconstantize-parameterize-titleize-underscore)
293
+ - [`underscore:`](#pluralize-singularize-camelize-demodulize-deconstantize-parameterize-titleize-underscore)
294
+ - [`demodulize:`](#pluralize-singularize-camelize-demodulize-deconstantize-parameterize-titleize-underscore)
295
+ - [`deconstantize:`](#pluralize-singularize-camelize-demodulize-deconstantize-parameterize-titleize-underscore)
296
+ - [`titleize:`](#pluralize-singularize-camelize-demodulize-deconstantize-parameterize-titleize-underscore)
297
+ - [`parameterize:`](#pluralize-singularize-camelize-demodulize-deconstantize-parameterize-titleize-underscore)
298
+
299
+ Arrays are not necessary for single values and if the rule contains only `arguments:` the keyword can be omitted, and everything moved up a level
300
+
301
+ ```yml
302
+ dynamic:
303
+ - name:
304
+ - send
305
+ calls:
306
+ arguments:
307
+ - 1
308
+ ```
309
+ is equivalent to:
310
+ ```yml
311
+ dynamic:
312
+ name: send
313
+ calls: 1
314
+ ```
315
+
316
+ ## `arguments:`
317
+ _alias `argument:`_
318
+
319
+ Each entry indicates the position or keyword of the value or values in either a list of method call arguments, or a literal array or hash
320
+ and when used in:
321
+ - [`calls:`](#calls-defines), [`defines:`](#calls-defines) or [`nested:`](#nested) to filter method/constant/variable names are supplied [`transforms:`](#transforms), or [`nested:`](#nested)
322
+ - [`add_prefix:`](#add_prefix), [`add_suffix:`](#add_suffix) the argument value can be used as the prefix/suffix rather than a literal string
323
+
324
+ It can have any of these properties:
325
+ - [`at:`](#at)
326
+ - [`has_value:`](#has_value)
327
+ - [`has_value_type:`](#has_value_type)
328
+
329
+ Arrays are not necessary for single values and if the rule contains only `at:` it can be omitted, and the values moved up a level
330
+
331
+ ## `has_arguments:`
332
+ _alias `has_argument:`_
333
+
334
+ Each entry indicates the position or keyword of the value or values in either a list of method call arguments, or a literal array or hash
335
+ and when used in:
336
+ - [`dynamic:`](#dynamic) it uses the presence of matching arguments to filter which methods calls/constant/variable assignments are processed
337
+ - [`keep:`](#keep) it uses the presence of matching arguments to filter which methods calls/constant/variable assignments are skipped
338
+
339
+ The method call/constant variable/assignment will be considered matching if it has **any** of these arguments/assigned values.
340
+
341
+ It can have any of these properties:
342
+ - [`at:`](#at)
343
+ - [`has_value:`](#has_value) # TODO
344
+ - [`has_value_type:`](#has_value_type) # TODO
345
+
346
+ Arrays are not necessary for single values and if the rule contains only `at:` it can be omitted, and the values moved up a level
347
+
348
+ ## `keywords:`
349
+ When the keyword argument **keywords** are the thing being called.
350
+
351
+ ```yml
352
+ dynamic:
353
+ - name: validates
354
+ calls:
355
+ - arguments: '*'
356
+ - keywords: '**'
357
+ camelize: true
358
+ add_suffix: Validator
359
+ ```
360
+ ```ruby
361
+ validates :first_name, :surname, presence: true
362
+ ```
363
+ will count calls for `validates`, `first_name`, `surname`, and `PresenceValidator`
364
+
365
+ ## `at:`
366
+
367
+ Each entry indicates the position or keyword of the value or values in either a list of method call arguments, or a literal array or hash.
368
+
369
+ This can be used in:
370
+ - [`has_arguments`](#has_arguments) to filter method calls/constant/variable assignments by the presence of this argument
371
+ - [`arguments:`](#arguments) to select positional argument values and keyword **values** to be processed
372
+ - [`keywords:`](#keywords) to select **keywords** and hash **keys** to be processed
373
+
374
+ Each entry can be any of:
375
+ - `'*'`: matches all positional arguments/array positions
376
+ - `'**'`: matches all keyword arguments/hash positions
377
+ - any positive Integer: matches the 1-indexed argument position/array position
378
+ - any other String: matches the keyword argument or hash value, where the keyword/hash key string or symbol
379
+ - or have at least one of the following properties to match the keyword/hash key string or symbol:
380
+ - [`has_prefix:`](#has_prefix)
381
+ - [`has_suffix:`](#has_suffix)
382
+ - [`matches:`](#matches) (this can't be used in the same entry as `has_prefix:` or `has_suffix:`)
383
+
384
+ Arrays are not necessary for single values
385
+
386
+ ## `has_value:`
387
+
388
+ filter [`arguments:`](#arguments), [`has_arguments:`](#has_arguments), and [`keywords:`](#keywords), by the argument/assigned value
389
+
390
+ Each entry can be one of
391
+ - `true`, `false`, `nil`, or an Integer. matches the literal value
392
+ - a String. matches the literal string or symbol value
393
+ - or have at least one of the following properties to match a string or symbol:
394
+ - [`has_prefix:`](#has_prefix)
395
+ - [`has_suffix:`](#has_suffix)
396
+ - [`matches:`](#matches) (this can't be used in the same entry as `has_prefix:` or `has_suffix:`)
397
+
398
+ ## `has_value_type:`
399
+
400
+ Filter [`arguments:`](#arguments), [`has_arguments:`](#has_arguments), and [`keywords:`](#keywords), by the argument/assigned value
401
+
402
+ Each entry can be one of
403
+ - `'String'`
404
+ - `'Symbol'`
405
+ - `'Integer'`
406
+ - `'Float'`
407
+
408
+ Arrays are not necessary for single values
409
+
410
+ ## `nested:`
411
+
412
+ To process an argument that is an array you will need to supply which entries in the array need processing.
413
+
414
+ This can be used in [`calls:`](#calls-defines), [`defines:`](#calls-defines) or within a preceding `nested:` entry
415
+
416
+ Each entry must be have at least one the these properties:
417
+ - [`arguments:`](#arguments)
418
+ - [`keywords:`](#keywords) keyword argument keywords, and assigned hash keys
419
+ And may additionally have any of these properties:
420
+ - [`nested:`](#nested)
421
+ - [`recursive: true`](#recursive-true)
422
+
423
+ It will filter the arrays/hashes found by its sibling properties `arguments:`, and `keywords:`, using its own child properties.
424
+
425
+ e.g.
426
+ ```yml
427
+ dynamic:
428
+ - names: my_method
429
+ calls:
430
+ argument: 2
431
+ nested:
432
+ argument: '*'
433
+ nested:
434
+ argument: name
435
+ ```
436
+ ```ruby
437
+ my_method([{name: :name_1}, {name: :name_2}, [{name: :name_3, value: :value_1}, {name: :name_4, value: :value_2}])
438
+ ```
439
+ will count `name_3`, and `name_4` (but not `name_1` and `name_2` because they're within the first positional argument, and not `value_1`, or `value_2`)
440
+
441
+ Arrays are not necessary for single values
442
+
443
+ ## `recursive: true`
444
+
445
+ This can be used in [`calls:`](#calls-defines), [`defines:`](#calls-defines) or within a preceding [`nested:`](#nested) entry
446
+
447
+ Similar to [`nested:`](#nested), this processes arrays hashes found by its sibling [`arguments:`](#arguments) and [`keywords:`](#keywords) properties, with those same properties, recursively.
448
+
449
+ e.g. rails' ActiveRecord::Base#joins method can call association methods using all positional arguments values, keyword argument keywords and values, and recursively all values arrays and hash keys and values
450
+ ```yml
451
+ dynamic:
452
+ name: [joins, left_joins]
453
+ calls:
454
+ arguments: ['*', '**']
455
+ keywords: '**'
456
+ recursive: true
457
+ ```
458
+
459
+ ## `itself: true`
460
+
461
+ Will supply the method/constant/variable name itself as the thing to be transformed.
462
+ The original method/constant/variable name will continue to be called/defined as normal
463
+
464
+ This can be used in [`calls:`](#calls-defines) and [`defines:`](#calls-defines)
465
+
466
+ ```yml
467
+ - name:
468
+ has_prefix: be_
469
+ calls:
470
+ itself: true
471
+ delete_prefix: be_
472
+ add_suffix: '?'
473
+ ```
474
+ ```ruby
475
+ expect(value).to be_empty
476
+ ```
477
+ will count `be_empty` as a call to `empty?`
478
+
479
+ ## `value:`
480
+
481
+ Will supply a literal string value method/constant/variable name itself as the thing to be called/defined.
482
+ This can be used in [`calls:`](#calls-defines) and [`defines:`](#calls-defines).
483
+
484
+ ```yml
485
+ - name: perform_async
486
+ calls:
487
+ value: perform
488
+ ```
489
+ ```ruby
490
+ MyWorker.perform_async
491
+ ```
492
+ will count `perform_async` as a call to `perform`
493
+
494
+ ## `transforms:`
495
+
496
+ Sometimes the method/class being called is modified from the literal argument, sometimes that's just appending an `=` and sometimes it's more complex:
497
+
498
+ Each entry can have a string that is an argumentless transform (e.g. capitalize) or a hash with transform keywords. This can be in used for entries in:
499
+ - [`defines:`](#calls-defines), [`calls:`](#calls-defines) to modify values filtered by the sibling keys [`arguments:`](#arguments), [`keywords:`](#keywords) and [`itself: true`](#itself-true) (also [`value:`](#value) but as that's a literal value anyway, why would you)
500
+ - [`add_prefix:`](#add_prefix-add_suffix), [`add_suffix:`](#add_prefix-add_suffix) to modify the selected `argument:` or `keyword:` for dynamic prefixes and suffixes
501
+
502
+ - [`original`](#original) or `original: true`
503
+ - [`add_prefix:`](#add_prefix-add_suffix)
504
+ - [`add_suffix:`](#add_prefix-add_suffix)
505
+ - [`delete_prefix:`](#delete_prefix-delete_suffix)
506
+ - [`delete_suffix:`](#delete_prefix-delete_suffix)
507
+ - [`delete_before:`](#delete_before-delete_after)
508
+ - [`delete_after:`](#delete_before-delete_after)
509
+ - [`split:`](#split)
510
+ - [`downcase`](#downcase-upcase-capitalize-swapcase) or `downcase: true`
511
+ - [`upcase`](#downcase-upcase-capitalize-swapcase) or `upcase: true`
512
+ - [`capitalize`](#downcase-upcase-capitalize-swapcase) or `capitalize: true`
513
+ - [`swapcase`](#downcase-upcase-capitalize-swapcase) or `swapcase: true`
514
+ - Also, if you have the active_support gem available:
515
+ - [`pluralize`](#pluralize-singularize-camelize-demodulize-deconstantize-parameterize-titleize-underscore) or `pluralize: true`
516
+ - [`singularize`](#pluralize-singularize-camelize-demodulize-deconstantize-parameterize-titleize-underscore) or `singularize: true`
517
+ - [`camelize`](#pluralize-singularize-camelize-demodulize-deconstantize-parameterize-titleize-underscore) or `camelize: true`
518
+ - [`underscore`](#pluralize-singularize-camelize-demodulize-deconstantize-parameterize-titleize-underscore) or `underscore: true`
519
+ - [`demodulize`](#pluralize-singularize-camelize-demodulize-deconstantize-parameterize-titleize-underscore) or `demodulize: true`
520
+ - [`deconstantize`](#pluralize-singularize-camelize-demodulize-deconstantize-parameterize-titleize-underscore) or `deconstantize: true`
521
+ - [`titleize`](#pluralize-singularize-camelize-demodulize-deconstantize-parameterize-titleize-underscore) or `titleize: true`
522
+ - [`parameterize`](#pluralize-singularize-camelize-demodulize-deconstantize-parameterize-titleize-underscore) or `parameterize: true`
523
+
524
+ If any one of these `transforms:` entries are used, all count as being used. To have these be counted independently instead, create multiple entries in the `defines:` list.
525
+
526
+ ```yml
527
+ - name: attribute
528
+ defines:
529
+ - argument: 1
530
+ transforms:
531
+ - original # no transformation
532
+ - add_suffix: '?'
533
+ - add_suffix: '='
534
+ ```
535
+ ```ruby
536
+ attribute :first_name
537
+ ```
538
+ will count as a definition of `first_name`, `first_name=` and `first_name?`. `firstname=` wouldn't be reported on, even if only `first_name` and `first_name?` were used.
539
+
540
+ Arrays are not necessary for single values, and if there is just one set of transforms, the `transforms:` keyword can be omitted and everything moved up a level.
541
+
542
+ ```yml
543
+ - name: attr_writer
544
+ defines:
545
+ - argument: '*'
546
+ add_suffix: '='
547
+ ```
548
+ ```ruby
549
+ attr_writer :first_name, :surname
550
+ ```
551
+ will count as the definition of `first_name=`, and `surname=`
552
+
553
+ ## `original`
554
+
555
+ Can be used in the [`transforms:`](#transforms) list, if used in a hash `true` can be used as a placeholder value
556
+
557
+ This performs no change, and is only useful when grouped with other transforms
558
+
559
+ ## `add_prefix:`, `add_suffix:`
560
+
561
+ Can be used in the [`transforms:`](#transforms) list (or anywhere `transforms:` is able to be omitted).
562
+
563
+ Each entry is one of:
564
+ - a literal string
565
+ - [`argument:`](#argument) or [`keyword:`](#keyword) matching arguments/assignments from the original method call/method definition
566
+ - Optionally [`transform:`] or any of the `transform:` properties, to transform the value(s) from `argument:` and `keyword:`.
567
+
568
+ if multiple transform results are possible (from multiple entries, or multiple matching arguments or etc), then all results will be used.
569
+
570
+ Arrays are not necessary for single values
571
+
572
+ ## `delete_prefix:`, `delete_suffix:`
573
+
574
+ Can be used in the [`transforms:`](#transforms) list (or anywhere `transforms:` is able to be omitted).
575
+
576
+ Each entry is a literal string, and _if present_ the matching prefix or suffix will be removed (if it's not present, this transform will continue to the next transform/result unmodified)
577
+
578
+ if multiple transform results are possible (from multiple entries), then all results will be used.
579
+
580
+ Arrays are not necessary for single values
581
+
582
+ ## `delete_before:`, `delete_after:`
583
+
584
+ Can be used in the [`transforms:`](#transforms) list (or anywhere `transforms:` is able to be omitted).
585
+
586
+ Each entry is a literal string, and _if present_ the string and everything before/after will be removed from the incoming value (if it's not present, this transform will continue to the next transform/result unmodified). if it's present multiple times then it will always be everything before/after the first substring match
587
+
588
+ if multiple transform results are possible (from multiple entries), then all results will be used.
589
+
590
+ Arrays are not necessary for single values
591
+
592
+ ## `split:`
593
+
594
+ Can be used in the [`transforms:`](#transforms) list (or anywhere `transforms:` is able to be omitted).
595
+
596
+ Each entry is a literal string, and _if present_ the incoming value will string will be split on this value. (if it's not present, this transform will continue to the next transform/result unmodified)
597
+
598
+ if multiple transform results are possible (from multiple entries), then all results will be used.
599
+
600
+ Arrays are not necessary for single values
601
+
602
+ ## `downcase`, `upcase`, `capitalize`, `swapcase`
603
+
604
+ Can be used in the [`transforms:`](#transforms) list (or anywhere `transforms:` is able to be omitted).
605
+ if used in a hash `true` can be used as a placeholder value
606
+
607
+ the incoming value will be transformed using the [core ruby String method](https://ruby-doc.org/core-2.6/String.html#method-i-capitalize)
608
+
609
+ ## `pluralize`, `singularize`, `camelize`, `demodulize`, `deconstantize`, `parameterize`, `titleize`, `underscore`
610
+ _aliases `camelcase`, `titlecase`_
611
+
612
+ Can be used in the [`transforms:`](#transforms) list (or anywhere `transforms:` is able to be omitted).
613
+ if used in a hash `true` can be used as a placeholder value
614
+
615
+ the incoming value will be transformed using the [active_support String core extensions](https://edgeguides.rubyonrails.org/active_support_core_extensions.html#inflections)
616
+ and if using [gems:](#gems) with `active_support` or `rails` then your `config/initializers/inflections.rb` will be loaded. if you have inflections in another file, then supply that to [`requires:`](#requires).
617
+
618
+ If the `active_support` gem is not available this will raise an error.
619
+
620
+ ## `unless:`
621
+
622
+ It will filter the values found by its sibling properties, whatever they are, removing anything that matches its own child properties.
623
+ if it has no sibling properties, then it's filtering everything to remove anything that matches it's own child properties.
624
+
625
+ Each entry is able to have child properties drawn from whatever the unless keyword's sibling properties are able to be
626
+
627
+ Arrays are not necessary for single values