Skip to content

Commit

Permalink
Add new MigrationFileName cop
Browse files Browse the repository at this point in the history
  • Loading branch information
j-miyake committed Feb 24, 2022
1 parent 2a76325 commit 167304e
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 0 deletions.
1 change: 1 addition & 0 deletions changelog/new_add_new_migrationfilename_cop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* [#644](https://github.com/rubocop/rubocop-rails/pull/644): Add new `MigrationFileName` cop. ([@johnny-miyake][])
7 changes: 7 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,13 @@ Rails/MatchRoute:
- config/routes.rb
- config/routes/**/*.rb

Rails/MigrationFileName:
Description: 'The class name of the migration should match its file name.'
Enabled: pending
VersionAdded: '<<next>>'
Include:
- db/migrate/*.rb

Rails/NegateInclude:
Description: 'Prefer `collection.exclude?(obj)` over `!collection.include?(obj)`.'
StyleGuide: 'https://rails.rubystyle.guide#exclude'
Expand Down
50 changes: 50 additions & 0 deletions lib/rubocop/cop/rails/migration_file_name.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Rails
# This cop makes sure that each migration file defines a migration class
# whose name matches the file name.
# (i.e. `20220224111111_create_users.rb` should define `CreateUsers` class.)
#
# @example
# # db/migrate/20220224111111_create_users.rb
#
# # bad
#
# class SellBooks < ActiveRecord::Migration[7.0]
# end
#
# # good
#
# class CreateUsers < ActiveRecord::Migration[7.0]
# end
#
class MigrationFileName < Base
MSG = 'The class name does not match with the file name.'

def on_new_investigation
filepath = processed_source.file_path
@basename = File.basename(filepath, '.rb')
end

def on_class(node)
class_name = node.loc.name.source
snake_class_name = to_snakecase(class_name)
basename_without_timestamp = @basename.sub(/^[0-9]+_/, '')
add_offense(node.loc.name) if snake_class_name != basename_without_timestamp
end

private

def to_snakecase(word)
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
word.tr!('-', '_')
word.downcase!
word
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/rubocop/cop/rails_cops.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
require_relative 'rails/link_to_blank'
require_relative 'rails/mailer_name'
require_relative 'rails/match_route'
require_relative 'rails/migration_file_name'
require_relative 'rails/negate_include'
require_relative 'rails/not_null_column'
require_relative 'rails/order_by_id'
Expand Down
35 changes: 35 additions & 0 deletions spec/rubocop/cop/rails/migration_file_name_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Rails::MigrationFileName, :config do
let(:filename) { 'db/migrate/20220101050505_create_users.rb' }

context 'when the file name matches its class' do
context 'with a super class' do
it do
expect_no_offenses(<<~RUBY, filename)
class CreateUsers < ActiveRecord::Migration[7.0]
end
RUBY
end
end

context 'without any super class' do
it do
expect_no_offenses(<<~RUBY, filename)
class CreateUsers
end
RUBY
end
end
end

context 'when the file name does not match its class' do
it do
expect_offense(<<~RUBY, filename)
class SellBooks < ActiveRecord::Migration[7.0]
^^^^^^^^^ The class name does not match with the file name.
end
RUBY
end
end
end

0 comments on commit 167304e

Please sign in to comment.