Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new cop for enum starting value #57

Merged
merged 11 commits into from
Jun 22, 2023
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

# main

* Added cop `Rails/EnumStartingValue` ([#57](https://github.com/petalmd/rubocop-petal/pull/57))

* Added cop `Migration/StandaloneAddReference` ([#54](https://github.com/petalmd/rubocop-petal/pull/54))
* Update `Migration/ChangeTableReferences` on send alias and message to handle removing references. ([#55](https://github.com/petalmd/rubocop-petal/pull/55))

Expand Down
7 changes: 7 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ Rails/EnumPrefix:
Include:
- app/models/**/*

Rails/EnumStartingValue:
Description: 'Prevent starting from zero with an enum.'
Enabled: true
StyleGuide: https://github.com/petalmd/rubocop-petal/issues/56
Include:
- app/models/**/*

Rails/RiskyActiverecordInvocation:
Description: 'Interpolation, use hash or parameterized syntax.'
Enabled: true
Expand Down
42 changes: 42 additions & 0 deletions lib/rubocop/cop/rails/enum_starting_value.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Rails
# Prevent the user to start from Zero with an enum.
# When using a wrong value like .where(state: :patato) let Rails + MySQL do a WHERE state = 0.
# It will match nothing since no record will have a 0 value.
#
# # bad
# enum my_enum: {apple: 0, bannana: 1}
#
# # good
# enum my_enum: {apple: 1, banana: 2}

class EnumStartingValue < Base
MSG = 'Prefer starting from `1` instead of `0` with `enum`.'

def_node_matcher :enum?, <<~PATTERN
(send nil? :enum (hash ...))
PATTERN

def_node_matcher :enum_attributes, <<~PATTERN
(send nil? :enum (:hash (:pair (...)$(...) )...))
PATTERN

def on_send(node)
return unless enum? node

add_offense(node) if start_with_zero?(enum_attributes(node))
end

def start_with_zero?(node)
node.children.any? do |child|
value = child.value
value.type == :int && value.value.zero?
end
end
end
end
end
end
79 changes: 79 additions & 0 deletions spec/rubocop/cop/rails/enum_starting_value_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Rails::EnumStartingValue, :config do
context 'without an enum' do
it 'expects no offense' do
expect_no_offenses(<<~RUBY)
puts 'some stuff'

# Declares a model without enum
class MyModel
has_many :some_other_thing
end
RUBY
end
end

context 'when starts from zero' do
context 'without prefix' do
it 'expects an offense' do
expect_offense(<<~RUBY)
class MyModel
enum my_enum: {state1: 0, state2: 2}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may sound weird, but can you add a specs to that check that no value should be 0.

Like

enum my_enum: {state1: 1, state2: 0}

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer starting from `1` instead of `0` with `enum`.
end
RUBY

expect_offense(<<~RUBY)
class MyModel
enum my_enum: {
^^^^^^^^^^^^^^^ Prefer starting from `1` instead of `0` with `enum`.
state1: 0,
state2: 2
}
end
RUBY
end
end

context 'with prefix' do
it 'expects an offense' do
expect_offense(<<~RUBY)
class MyModel
enum my_enum: {state1: 0, state2: 2}, _suffix: false
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer starting from `1` instead of `0` with `enum`.
end
RUBY

expect_offense(<<~RUBY)
class MyModel
enum my_enum: {
^^^^^^^^^^^^^^^ Prefer starting from `1` instead of `0` with `enum`.
state1: 0,
state2: 2
}, _prefix: true
end
RUBY
end
end
end

context 'when starts from a non zero' do
it 'expects no offense' do
expect_no_offenses(<<~RUBY)
class MyModel
enum my_enum: {state1: 1, state2: 2}, _prefix: true
end
RUBY

expect_no_offenses(<<~RUBY)
class MyModel
enum my_enum: {
state1: 2,
state2: 3
}
end
RUBY
end
end
end