-
-
Notifications
You must be signed in to change notification settings - Fork 47
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
Implement Enum Column checker #192
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# frozen_string_literal: true | ||
|
||
module DatabaseConsistency | ||
module Checkers | ||
# This class checks that ActiveRecord enum is backed by enum column | ||
class EnumColumnChecker < EnumChecker | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now, this checker intersects with I don't think we should have a single checker as this could make code harder, but (I believe) you would agree that having both checkers enabled simultaneously doesn't make much sense. From one side, this looks more like a mode (strict/open). However, the gem doesn't support modes per checkers, and I'm wondering if we should have such (similar to what Rubocop has, for example). If we don't go in that direction, maybe we should have an internal toggle system that turns off/on some checkers based on intersections. For example, if this checker is on, then With that said, I tend to have modes but I would like to hear your opinion on this matter. |
||
Report = ReportBuilder.define( | ||
DatabaseConsistency::Report, | ||
:table_name, | ||
:column_name | ||
) | ||
|
||
private | ||
|
||
# ActiveRecord supports native enum type since version 7 and only for PostgreSQL | ||
def preconditions | ||
Helper.postgresql? && ActiveRecord::VERSION::MAJOR >= 7 && column.present? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I remember we discussed enabling this checker only for PostgreSQL 15+, but let me do some benchmarks for earlier versions as well. I think we should stick to "postgres and AR >=7" condition here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This would be awesome! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, we can do that 👍 |
||
end | ||
|
||
def check | ||
if valid? | ||
report_template(:ok) | ||
else | ||
report_template(:fail, error_slug: :enum_column_type_mismatch) | ||
end | ||
end | ||
|
||
def report_template(status, error_slug: nil) | ||
Report.new( | ||
status: status, | ||
error_slug: error_slug, | ||
error_message: nil, | ||
table_name: model.table_name, | ||
column_name: column.name, | ||
**report_attributes | ||
) | ||
end | ||
|
||
def column | ||
@column ||= model.columns.find { |c| c.name.to_s == enum.to_s } | ||
end | ||
|
||
# @return [Boolean] | ||
def valid? | ||
column.type == :enum | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# frozen_string_literal: true | ||
|
||
module DatabaseConsistency | ||
module Writers | ||
module Simple | ||
class EnumColumn < Base # :nodoc: | ||
private | ||
|
||
def template | ||
'column should be enum type' | ||
end | ||
|
||
def unique_attributes | ||
{ | ||
table_name: report.table_name, | ||
column_name: report.column_name | ||
} | ||
end | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
# frozen_string_literal: true | ||
|
||
RSpec.describe DatabaseConsistency::Checkers::EnumColumnChecker, :postgresql do | ||
subject(:checker) { described_class.new(model, enum) } | ||
|
||
let(:model) { entity_class } | ||
let(:enum) { entity_class.defined_enums.keys.first } | ||
|
||
context 'with enum column' do | ||
let(:entity_class) do | ||
define_class do |klass| | ||
klass.enum field: { value1: 'value1', value2: 'value2' } | ||
end | ||
end | ||
|
||
before do | ||
define_database do | ||
create_enum :field_type, %w[value1 value2] | ||
|
||
create_table :entities do |t| | ||
t.enum :field, enum_type: 'field_type' | ||
end | ||
end | ||
end | ||
|
||
specify do | ||
expect(checker.report).to have_attributes( | ||
checker_name: 'EnumColumnChecker', | ||
table_or_model_name: entity_class.name, | ||
column_or_attribute_name: 'field', | ||
status: :ok, | ||
error_message: nil, | ||
error_slug: nil | ||
) | ||
end | ||
end | ||
|
||
context 'with integer column' do | ||
let(:entity_class) do | ||
define_class do |klass| | ||
klass.enum field: %i[value1 value2] | ||
end | ||
end | ||
|
||
before do | ||
define_database do | ||
create_table :entities do |t| | ||
t.integer :field | ||
end | ||
end | ||
end | ||
|
||
specify do | ||
expect(checker.report).to have_attributes( | ||
checker_name: 'EnumColumnChecker', | ||
table_or_model_name: entity_class.name, | ||
column_or_attribute_name: 'field', | ||
status: :fail, | ||
error_message: nil, | ||
error_slug: :enum_column_type_mismatch | ||
) | ||
end | ||
end | ||
|
||
context 'with string column' do | ||
let(:entity_class) do | ||
define_class do |klass| | ||
klass.enum field: { value1: 'value1', value2: 'value2' } | ||
end | ||
end | ||
|
||
before do | ||
define_database do | ||
create_table :entities do |t| | ||
t.string :field | ||
end | ||
end | ||
end | ||
|
||
specify do | ||
expect(checker.report).to have_attributes( | ||
checker_name: 'EnumColumnChecker', | ||
table_or_model_name: entity_class.name, | ||
column_or_attribute_name: 'field', | ||
status: :fail, | ||
error_message: nil, | ||
error_slug: :enum_column_type_mismatch | ||
) | ||
end | ||
end | ||
|
||
context 'when ActiveRecord below 7' do | ||
toydestroyer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
before do | ||
stub_const('ActiveRecord::VERSION::MAJOR', 6) | ||
end | ||
|
||
context 'with integer column' do | ||
let(:entity_class) do | ||
define_class do |klass| | ||
klass.enum field: %i[value1 value2] | ||
end | ||
end | ||
|
||
before do | ||
define_database do | ||
create_table :entities do |t| | ||
t.integer :field | ||
end | ||
end | ||
end | ||
|
||
specify do | ||
expect(checker.report).to be_nil | ||
end | ||
end | ||
|
||
context 'with string column' do | ||
let(:entity_class) do | ||
define_class do |klass| | ||
klass.enum field: { value1: 'value1', value2: 'value2' } | ||
end | ||
end | ||
|
||
before do | ||
define_database do | ||
create_table :entities do |t| | ||
t.string :field | ||
end | ||
end | ||
end | ||
|
||
specify do | ||
expect(checker.report).to be_nil | ||
end | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should it be in
enum_checkers
folder orcolumn_checkers
? What's the difference?Currently there are
column_checkers/enum_value_checker.rb
andenum_checkers/enum_type_checker.rb
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It depends on what you want to iterate in the "processor". One is iterating through
columns
, another throughdefined_enums
.In
enum_value_checker
, we check that column with theenum
type has corresponding Ruby values (through inclusion validator or enum). So the subject is acolumn
.enum_type_checker.rb
focuses onenum
with the corresponding column type.Therefore, I think it should be
EnumChecker
as you want to ensure that everyenum
is covered by theenum
column.