forked from rubocop/rubocop-rails
/
enum_hash.rb
65 lines (57 loc) · 1.83 KB
/
enum_hash.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# frozen_string_literal: true
module RuboCop
module Cop
module Rails
# This cop looks for enums written with array syntax.
#
# When using array syntax, adding an element in a
# position other than the last causes all previous
# definitions to shift. Explicitly specifying the
# value for each key prevents this from happening.
#
# @example
# # bad
# enum status: [:active, :archived]
#
# # good
# enum status: { active: 0, archived: 1 }
#
class EnumHash < Cop
MSG = 'Enum defined as an array found in `%<enum>s` enum declaration. '\
'Use hash syntax instead.'
def_node_matcher :enum_with_array?, <<~PATTERN
(send nil? :enum (hash (pair (_ $_) array)))
PATTERN
def on_send(node)
enum_with_array?(node) do |name|
add_offense(node, message: format(MSG, enum: name))
end
end
def autocorrect(node)
enum_values = node.children[2].children.first.children[1]
to_replace = enum_values.loc.expression
values_hash = "{ #{converted_values(enum_values)} }"
->(corrector) { corrector.replace(to_replace, values_hash) }
end
private
def converted_values(enum_values)
enum_values.children.each_with_index.map do |child, index|
hash_entry_as_string(child, index)
end.join(', ')
end
def hash_entry_as_string(child, index)
value = child.children.first
case value
when String
"'#{value}' => #{index}"
when Symbol
value = "'#{value}'" if value.match(/\s/)
"#{value}: #{index}"
else
"#{child.source} => #{index}"
end
end
end
end
end
end