leftovers 0.2.2 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +41 -0
  3. data/README.md +35 -49
  4. data/docs/Configuration.md +627 -0
  5. data/exe/leftovers +2 -2
  6. data/leftovers.gemspec +13 -7
  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 +160 -97
  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 +9 -0
  21. data/lib/config/will_paginate.yml +12 -14
  22. data/lib/leftovers.rb +61 -43
  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 +17 -14
  28. data/lib/leftovers/collector.rb +7 -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 +82 -62
  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 +24 -14
  82. data/lib/leftovers/parser.rb +2 -5
  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 +1 -1
  102. data/lib/leftovers/value_processors.rb +40 -0
  103. data/lib/leftovers/value_processors/add_dynamic_prefix.rb +31 -0
  104. data/lib/leftovers/value_processors/add_dynamic_suffix.rb +31 -0
  105. data/lib/leftovers/value_processors/add_prefix.rb +20 -0
  106. data/lib/leftovers/value_processors/add_suffix.rb +20 -0
  107. data/lib/leftovers/value_processors/camelize.rb +24 -0
  108. data/lib/leftovers/value_processors/capitalize.rb +19 -0
  109. data/lib/leftovers/value_processors/deconstantize.rb +24 -0
  110. data/lib/leftovers/value_processors/delete_after.rb +22 -0
  111. data/lib/leftovers/value_processors/delete_before.rb +22 -0
  112. data/lib/leftovers/value_processors/delete_prefix.rb +26 -0
  113. data/lib/leftovers/value_processors/delete_suffix.rb +26 -0
  114. data/lib/leftovers/value_processors/demodulize.rb +24 -0
  115. data/lib/leftovers/value_processors/downcase.rb +19 -0
  116. data/lib/leftovers/value_processors/each.rb +21 -0
  117. data/lib/leftovers/value_processors/each_for_definition_set.rb +29 -0
  118. data/lib/leftovers/value_processors/each_keyword.rb +27 -0
  119. data/lib/leftovers/value_processors/each_keyword_argument.rb +27 -0
  120. data/lib/leftovers/value_processors/each_positional_argument.rb +24 -0
  121. data/lib/leftovers/value_processors/itself.rb +17 -0
  122. data/lib/leftovers/value_processors/keyword.rb +36 -0
  123. data/lib/leftovers/value_processors/keyword_argument.rb +36 -0
  124. data/lib/leftovers/value_processors/parameterize.rb +24 -0
  125. data/lib/leftovers/value_processors/placeholder.rb +18 -0
  126. data/lib/leftovers/value_processors/pluralize.rb +24 -0
  127. data/lib/leftovers/value_processors/positional_argument.rb +26 -0
  128. data/lib/leftovers/value_processors/replace_value.rb +18 -0
  129. data/lib/leftovers/value_processors/return_definition.rb +26 -0
  130. data/lib/leftovers/value_processors/return_string.rb +14 -0
  131. data/lib/leftovers/value_processors/singularize.rb +24 -0
  132. data/lib/leftovers/value_processors/split.rb +22 -0
  133. data/lib/leftovers/value_processors/swapcase.rb +19 -0
  134. data/lib/leftovers/value_processors/titleize.rb +24 -0
  135. data/lib/leftovers/value_processors/underscore.rb +24 -0
  136. data/lib/leftovers/value_processors/upcase.rb +19 -0
  137. data/lib/leftovers/version.rb +1 -1
  138. metadata +190 -28
  139. data/lib/config/selenium.yml +0 -21
  140. data/lib/leftovers/argument_rule.rb +0 -219
  141. data/lib/leftovers/core_ext.rb +0 -13
  142. data/lib/leftovers/hash_rule.rb +0 -40
  143. data/lib/leftovers/name_rule.rb +0 -53
  144. data/lib/leftovers/rule.rb +0 -74
  145. data/lib/leftovers/transform_rule.rb +0 -171
  146. data/lib/leftovers/value_rule.rb +0 -56
@@ -1,21 +0,0 @@
1
- rules:
2
- skip: true
3
- name:
4
- - before_navigate_to
5
- - before_navigate_back
6
- - after_navigate_back
7
- - before_navigate_forward
8
- - after_navigate_forward
9
- - before_find
10
- - after_find
11
- - before_click
12
- - before_change_value_of
13
- - after_change_value_of
14
- - before_execute_script
15
- - after_execute_script
16
- - before_quit
17
- - before_close
18
- - after_navigate_to
19
- - after_click
20
- - after_quit
21
- - after_close
@@ -1,219 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'definition'
4
- require_relative 'definition_set'
5
- require_relative 'name_rule'
6
- require_relative 'transform_rule'
7
- require_relative 'hash_rule'
8
-
9
- module Leftovers
10
- class ArgumentRule # rubocop:disable Metrics/ClassLength
11
- def self.wrap(rules, definer: false) # rubocop:disable Metrics/MethodLength
12
- case rules
13
- when Array
14
- rules.flat_map { |r| wrap(r, definer: definer) }
15
- when Hash
16
- [new(**rules, definer: definer)]
17
- else
18
- []
19
- end
20
- end
21
-
22
- ADDITIONAL_VALID_KEYS = Leftovers::TransformRule::VALID_TRANSFORMS + %i{if unless}
23
- def initialize( # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength, Metrics/CyclomaticComplexity
24
- argument: nil,
25
- arguments: nil,
26
- key: nil,
27
- keys: nil,
28
- itself: false,
29
- linked_transforms: nil,
30
- transforms: nil,
31
- definer: false,
32
- **options
33
- )
34
- assert_valid_keys(options, ADDITIONAL_VALID_KEYS)
35
- prepare_argument(argument, arguments)
36
- @key = prepare_key(key, keys)
37
- @itself = itself
38
-
39
- unless @positions || @keywords || @all_positions || @all_keywords || @key || @itself
40
- raise ArgumentError, "require at least one of 'argument(s)', 'key(s)', itself"
41
- end
42
-
43
- @if = prepare_condition(options.delete(:if))
44
- @unless = prepare_condition(options.delete(:unless))
45
- @transforms = prepare_transform(options, transforms, linked_transforms)
46
- @definer = definer
47
- end
48
-
49
- def prepare_transform(options, transforms, linked_transforms) # rubocop:disable Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize
50
- if linked_transforms && transforms
51
- raise ArgumentError, 'Only use one of linked_transforms/transforms'
52
- end
53
- return if !linked_transforms && !transforms && options.empty?
54
-
55
- if !(linked_transforms || transforms)
56
- @transform = TransformRule.new(options)
57
- else
58
- @linked = !!linked_transforms
59
-
60
- transforms = (linked_transforms || transforms).map do |transform|
61
- transform = { transform.to_sym => true } if transform.is_a?(String)
62
- Leftovers::TransformRule.new(options.merge(transform))
63
- end
64
-
65
- if transforms.length <= 1
66
- @transform = transforms.first
67
- else
68
- @transforms = transforms
69
- end
70
- end
71
- end
72
-
73
- def prepare_condition(conditions)
74
- Leftovers.array_wrap(conditions).each do |cond|
75
- cond[:has_argument] = HashRule.new(cond[:has_argument]) if cond[:has_argument]
76
- end
77
- end
78
-
79
- def prepare_key(key, keys)
80
- raise ArgumentError, 'Only use one of key/keys' if key && keys
81
-
82
- key || keys
83
- end
84
-
85
- def prepare_argument(argument, arguments) # rubocop:disable Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/AbcSize
86
- raise ArgumentError, 'Only use one of argument/arguments' if argument && arguments
87
-
88
- positions = Set.new
89
- keywords = []
90
-
91
- Leftovers.each_or_self(argument || arguments) do |arg|
92
- case arg
93
- when '*'
94
- @all_positions = true
95
- when '**'
96
- @all_keywords = true
97
- when Integer
98
- positions << arg - 1
99
- when String, Symbol, Hash
100
- keywords << arg
101
- end
102
- end
103
-
104
- @positions = positions unless @all_positions || positions.empty? || @all_positions
105
- @keywords = NameRule.new(keywords) unless @all_keywords || keywords.empty?
106
- end
107
-
108
- def matches(method_node) # rubocop:disable Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize
109
- return [].freeze unless all_conditions_match?(method_node)
110
-
111
- result = []
112
-
113
- if @all_positions
114
- result.leftovers_append values(method_node.positional_arguments, method_node)
115
- elsif @positions
116
- result.leftovers_append(
117
- values(method_node.positional_arguments_at(@positions).compact, method_node)
118
- )
119
- end
120
-
121
- if @keywords || @all_keywords || @key
122
- result.leftovers_append hash_values(method_node.kwargs, method_node)
123
- end
124
- result << method_value(method_node) if @itself
125
-
126
- result
127
- end
128
-
129
- def values(value_nodes, method_node)
130
- value_nodes.flat_map { |value_node| value(value_node, method_node) }.compact
131
- end
132
-
133
- def all_conditions_match?(method_node)
134
- @if.all? { |c| condition_match?(c, method_node) } &&
135
- @unless.all? { |c| !condition_match?(c, method_node) }
136
- end
137
-
138
- def condition_match?(condition, method_name)
139
- return unless condition[:has_argument]
140
-
141
- hash_node = method_name.kwargs
142
- return false unless hash_node
143
-
144
- hash_node.each_pair.any? { |key, value| condition[:has_argument].match_pair?(key, value) }
145
- end
146
-
147
- def hash_values(hash_node, method_node) # rubocop:disable Metrics/MethodLength
148
- return unless hash_node
149
-
150
- value_nodes = []
151
- value_nodes.concat hash_node.keys if @key
152
-
153
- if @all_keywords
154
- value_nodes.concat(hash_node.values)
155
- elsif @keywords
156
- value_nodes.concat(hash_node.values_at_match(@keywords))
157
- end
158
-
159
- values(value_nodes, method_node)
160
- end
161
-
162
- def value(value_node, method_node) # rubocop:disable Metrics/MethodLength
163
- return unless value_node
164
-
165
- value_node = value_node.unwrap_freeze
166
-
167
- case value_node.type
168
- when :array
169
- values(value_node.values, method_node)
170
- when :hash
171
- hash_values(value_node, method_node)
172
- when :str, :sym
173
- symbol_values(value_node, method_node)
174
- end
175
- end
176
-
177
- SPLIT = /[.:]+/.freeze
178
- def symbol_values(symbol_node, method_node) # rubocop:disable Metrics/MethodLength
179
- subnodes = Array(transform(symbol_node.to_s, method_node))
180
- .flat_map { |s| s.to_s.split(SPLIT).map(&:to_sym) }
181
-
182
- return subnodes unless @definer
183
-
184
- location = symbol_node.loc.expression
185
- if @linked
186
- Leftovers::DefinitionSet.new(subnodes, location: location, method_node: method_node)
187
- else
188
- subnodes.map do |subnode|
189
- Leftovers::Definition.new(subnode, location: location, method_node: method_node)
190
- end
191
- end
192
- end
193
-
194
- def method_value(method_node)
195
- value = transform(method_node.to_s, method_node)
196
-
197
- return value unless @definer
198
-
199
- Leftovers::Definition.new(value, method_node: method_node)
200
- end
201
-
202
- def transform(string, method_node)
203
- return string unless @transform || @transforms
204
- return @transform.transform(string, method_node) if @transform
205
-
206
- @transforms.map do |transform|
207
- transform.transform(string, method_node)
208
- end
209
- end
210
-
211
- def assert_valid_keys(options, keys)
212
- invalid = options.keys - keys
213
-
214
- return if invalid.empty?
215
-
216
- raise ArgumentError, "unknown keyword#{'s' if invalid.length > 1}: #{invalid.join(', ')}"
217
- end
218
- end
219
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'set'
4
-
5
- class Array
6
- def leftovers_append(other)
7
- case other
8
- when Array, Set then concat(other)
9
- when nil then self
10
- else self.<< other
11
- end
12
- end
13
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'set'
4
- require_relative 'value_rule'
5
- require_relative 'name_rule'
6
-
7
- module Leftovers
8
- class HashRule
9
- def initialize(patterns) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
10
- keys = []
11
- pairs = []
12
- Leftovers.each_or_self(patterns) do |pat|
13
- if pat.is_a?(Hash) && pat[:value]
14
- pairs << [
15
- (NameRule.new(pat[:keyword]) if pat[:keyword]),
16
- (ValueRule.new(pat[:value]) if pat[:value])
17
- ]
18
- else
19
- keys << NameRule.new(pat)
20
- end
21
- end
22
-
23
- @keys = (NameRule.new(keys) if keys)
24
-
25
- @pairs = (pairs unless pairs.empty?)
26
-
27
- freeze
28
- end
29
-
30
- def match_pair?(key_node, value_node)
31
- return true if @keys&.match?(key_node.to_sym, key_node.to_s)
32
-
33
- @pairs&.any? do |(key_rule, value_rule)|
34
- next unless !key_rule || key_rule.match?(key_node.to_sym, key_node.to_s)
35
-
36
- (!value_rule || value_rule.match?(value_node))
37
- end
38
- end
39
- end
40
- end
@@ -1,53 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'set'
4
- module Leftovers
5
- class NameRule
6
- attr_reader :sym, :syms, :regexp
7
-
8
- def initialize(patterns) # rubocop:disable Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/AbcSize
9
- regexps = []
10
- syms = Set.new
11
- Leftovers.each_or_self(patterns) do |pat|
12
- case pat
13
- when Leftovers::NameRule
14
- syms.merge(pat.sym) if pat.sym
15
- syms.merge(pat.syms) if pat.syms
16
- regexps.concat(pat.regexp) if pat.regexp
17
- when String
18
- syms.merge(pat.split(/\s+/).map(&:to_sym))
19
- when Hash
20
- if pat[:match]
21
- regexps << /\A#{pattern[:match]}\z/
22
- elsif pat[:has_prefix] && pat[:has_suffix]
23
- regexps << /\A#{Regexp.escape(pat[:has_prefix])}.*#{Regexp.escape(pat[:has_suffix])}\z/
24
- elsif pat[:has_prefix]
25
- regexps << /\A#{Regexp.escape(pat[:has_prefix])}/
26
- elsif pat[:has_suffix]
27
- regexps << /#{Regexp.escape(pat[:has_suffix])}\z/
28
- end
29
- end
30
- end
31
-
32
- if syms.length <= 0
33
- @sym = syms.first
34
- @syms = nil
35
- else
36
- @sym = nil
37
- @syms = syms
38
- end
39
-
40
- @regexp = if regexps.empty?
41
- nil
42
- else
43
- Regexp.union(regexps)
44
- end
45
-
46
- freeze
47
- end
48
-
49
- def match?(sym, string)
50
- @sym&.==(sym) || @syms&.include?(sym) || @regexp&.match?(string)
51
- end
52
- end
53
- end
@@ -1,74 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'name_rule'
4
- require_relative 'argument_rule'
5
- require 'fast_ignore'
6
-
7
- module Leftovers
8
- class Rule
9
- def self.wrap(rules)
10
- case rules
11
- when Array then rules.flat_map { |r| wrap(r) }
12
- when nil then [].freeze
13
- else new(**rules)
14
- end
15
- end
16
-
17
- attr_reader :skip
18
- alias_method :skip?, :skip
19
-
20
- def initialize( # rubocop:disable Metrics/ParameterLists, Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/AbcSize
21
- name: nil,
22
- names: nil,
23
- calls: nil,
24
- call: nil,
25
- skip: false,
26
- defines: nil,
27
- define: nil,
28
- path: nil,
29
- paths: nil
30
- )
31
- raise ArgumentError, 'Only use one of name/names' if name && names
32
- raise ArgumentError, 'Only use one of path/paths' if path && paths
33
- raise ArgumentError, 'Only use one of call/calls' if call && calls
34
- raise ArgumentError, 'Only use one of define/defines' if define && defines
35
- if skip && (defines || calls)
36
- raise ArgumentError, "skip can't exist with defines or calls for #{name || names}"
37
- end
38
-
39
- @name_matcher = NameRule.new(name || names)
40
- @path = FastIgnore.new(include_rules: path || paths, gitignore: false) if path || paths
41
- @skip = skip
42
-
43
- begin
44
- @calls = ArgumentRule.wrap(calls)
45
- rescue ArgumentError => e
46
- raise e, "#{e.message} for calls for #{name}", e.backtrace
47
- end
48
-
49
- begin
50
- @defines = ArgumentRule.wrap(defines, definer: true)
51
- rescue ArgumentError => e
52
- raise e, "#{e.message} for defines for #{name}", e.backtrace
53
- end
54
- end
55
-
56
- def filename?(file)
57
- return true unless @path
58
-
59
- @path.allowed?(file)
60
- end
61
-
62
- def match?(name, name_s, file)
63
- @name_matcher.match?(name, name_s) && filename?(file)
64
- end
65
-
66
- def calls(node)
67
- @calls.flat_map { |m| m.matches(node) }
68
- end
69
-
70
- def definitions(node)
71
- @defines.flat_map { |m| m.matches(node) }
72
- end
73
- end
74
- end
@@ -1,171 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Leftovers
4
- class TransformRule # rubocop:disable Metrics/ClassLength
5
- # :nocov:
6
- using ::Leftovers::StringDeletePrefixSuffix if defined?(::Leftovers::StringDeletePrefixSuffix)
7
- # :nocov:
8
-
9
- RUBY_STRING_METHODS = %i{
10
- downcase
11
- upcase
12
- capitalize
13
- swapcase
14
- }.freeze
15
-
16
- ACTIVESUPPORT_STRING_METHODS = %i{
17
- pluralize
18
- singularize
19
- camelize
20
- camelcase
21
- underscore
22
- titleize
23
- titlecase
24
- demodulize
25
- deconstantize
26
- parameterize
27
- }.freeze
28
-
29
- CUSTOM_TRANSFORMS = %i{
30
- original
31
- delete_before
32
- delete_after
33
- add_prefix
34
- add_suffix
35
- delete_prefix
36
- delete_suffix
37
- replace_with
38
- }.freeze
39
-
40
- VALID_TRANSFORMS = RUBY_STRING_METHODS + ACTIVESUPPORT_STRING_METHODS + CUSTOM_TRANSFORMS
41
- # more possible transformations
42
- # gsub sub tr tr_s
43
- def initialize(transforms)
44
- @transforms = prepare_transforms(transforms)
45
-
46
- freeze
47
- end
48
-
49
- def transform(original_string, method_node)
50
- string = original_string
51
- @transforms.each { |proc| string = proc.call(string, method_node) }
52
-
53
- string.to_sym
54
- end
55
-
56
- private
57
-
58
- def prepare_transforms(transforms) # rubocop:disable Metrics/MethodLength
59
- transforms.map do |key, value|
60
- unless VALID_TRANSFORMS.include?(key)
61
- raise ArgumentError, <<~MESSAGE
62
- invalid transform key: (#{key}: #{value}).
63
- Valid transform keys are #{ALL_VALID_TRANSFORMS}"
64
- MESSAGE
65
- end
66
-
67
- key, value = prepare_hash_value(key, value) if value.is_a?(Hash)
68
-
69
- instance_variable_set(:"@#{key}", value.freeze)
70
- method(key)
71
- end
72
- end
73
-
74
- HASH_VALUE_TRANSFORMS = %i{add_prefix add_suffix}.freeze
75
- HASH_VALUE_KEYS = %i{from_argument joiner}.freeze
76
- def prepare_hash_value(method, hash) # rubocop:disable Metrics/MethodLength
77
- raise ArgumentError, <<~MESSAGE unless HASH_VALUE_TRANSFORMS.include?(method)
78
- invalid transform value (#{key}: #{value}).
79
- Hash values are only valid for #{HASH_VALUE_TRANSFORMS}
80
- MESSAGE
81
-
82
- hash = hash.map do |k, v|
83
- raise ArgumentError, <<~MESSAGE unless HASH_VALUE_KEYS.include?(k)
84
- invalid transform value argument (#{method}: { #{k}: #{v} }).
85
- Hash values are only valid for #{HASH_VALUE_TRANSFORMS}
86
- MESSAGE
87
-
88
- [k, v.to_sym]
89
- end.to_h
90
-
91
- ["#{method}_dynamic", hash]
92
- end
93
-
94
- RUBY_STRING_METHODS.each do |method|
95
- class_eval <<~RUBY, __FILE__, __LINE__ + 1
96
- def #{method}(string, _method_node)
97
- string.#{method}
98
- end
99
- RUBY
100
- end
101
-
102
- # leftovers:call activesupport_available?
103
- ACTIVESUPPORT_STRING_METHODS.each do |method|
104
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
105
- def #{method}(string, _method_node)
106
- return string unless activesupport_available?(:#{method})
107
-
108
- string.#{method}
109
- end
110
- RUBY
111
- end
112
-
113
- def original(string, _method_node)
114
- string
115
- end
116
-
117
- def delete_prefix(string, _method_node)
118
- string.delete_prefix(@delete_prefix)
119
- end
120
-
121
- def delete_suffix(string, _method_node)
122
- string.delete_suffix(@delete_suffix)
123
- end
124
-
125
- def delete_before(string, _method_node)
126
- string.split(@delete_before, 2)[1] || string
127
- end
128
-
129
- def delete_after(string, _method_node)
130
- string.split(@delete_after, 2).first
131
- end
132
-
133
- def replace_with(_string, _method_node)
134
- @replace_with
135
- end
136
-
137
- def add_prefix(string, _method_node)
138
- "#{@add_prefix}#{string}"
139
- end
140
-
141
- def add_suffix(string, _method_node)
142
- "#{string}#{@add_suffix}"
143
- end
144
-
145
- def add_prefix_dynamic(string, method_node)
146
- "#{dynamic_value(@add_prefix_dynamic, method_node)}#{@add_prefix_dynamic[:joiner]}#{string}"
147
- end
148
-
149
- def add_suffix_dynamic(string, method_node)
150
- "#{string}#{@add_suffix_dynamic[:joiner]}#{dynamic_value(@add_suffix_dynamic, method_node)}"
151
- end
152
-
153
- def dynamic_value(value, method_node)
154
- method_node.kwargs[value[:from_argument]].to_s if value[:from_argument]
155
- end
156
-
157
- def activesupport_available?(method) # rubocop:disable Metrics/MethodLength
158
- Leftovers.try_require(
159
- 'active_support/core_ext/string', 'active_support/inflections',
160
- message: <<~MESSAGE
161
- Tried transforming a string using an activesupport method (#{method}), but the activesupport gem was not available
162
- `gem install activesupport`
163
- MESSAGE
164
- )
165
-
166
- Leftovers.try_require(::File.join(Leftovers.pwd, 'config', 'initializers', 'inflections.rb'))
167
-
168
- defined?(ActiveSupport)
169
- end
170
- end
171
- end