10 files changed +119
-0
lines changed Original file line number Diff line number Diff line change 12
12
require 'dbcop/cop/belongs_to/foreign_key'
13
13
require 'dbcop/cop/validation/presence'
14
14
require 'dbcop/cop/validation/inclusion'
15
+ require 'dbcop/cop/has_many/foreign_key'
15
16
16
17
# Base module for the gem
17
18
module Dbcop
Original file line number Diff line number Diff line change @@ -15,6 +15,8 @@ def call
15
15
private
16
16
17
17
def valid?
18
+ return true unless connection . table_exists? ( model . table_name )
19
+
18
20
@valid ||= !connection . primary_keys ( model . table_name ) . map! do |pk |
19
21
column_valid? ( pk )
20
22
end . include? ( false )
Original file line number Diff line number Diff line change
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../cop'
4
+
5
+ module Dbcop
6
+ module HasMany
7
+ # Checks foreign key presence to the parent table of belongs_to association
8
+ class ForeignKey < Cop
9
+ def call
10
+ results = associations . map do |association |
11
+ valid? ( association )
12
+ end
13
+
14
+ results . none? ( &:! )
15
+ end
16
+
17
+ private
18
+
19
+ def valid? ( association )
20
+ success = foreign_key? ( association )
21
+ unless success
22
+ from_table = association . class_name . constantize . table_name
23
+ log ( "has_many #{ association . name } but has no foreign key from #{ from_table } .id" )
24
+ end
25
+ progress ( success , 'F' )
26
+
27
+ success
28
+ rescue NameError
29
+ log ( "Error processing #{ model . name } .#{ association . name } " )
30
+ end
31
+
32
+ def associations
33
+ model
34
+ . _reflections
35
+ . values
36
+ . select { |association | association . is_a? ( ActiveRecord ::Reflection ::HasManyReflection ) }
37
+ . reject ( &:polymorphic? )
38
+ end
39
+
40
+ def foreign_key? ( association )
41
+ to_table = model . table_name
42
+ connection . foreign_keys ( association . class_name . constantize . table_name ) . any? do |foreign_key |
43
+ foreign_key . to_table == to_table && foreign_key . options . fetch ( :primary_key ) == 'id'
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
Original file line number Diff line number Diff line change @@ -35,6 +35,8 @@ def validators
35
35
end
36
36
37
37
def find_column ( attribute )
38
+ return nil unless connection . table_exists? ( model . table_name )
39
+
38
40
connection
39
41
. columns ( model . table_name )
40
42
. find { |col | col . name == attribute . to_s }
Original file line number Diff line number Diff line change
1
+ # frozen_string_literal: true
2
+
3
+ module HasMany
4
+ def self . table_name_prefix
5
+ 'has_many_'
6
+ end
7
+ end
Original file line number Diff line number Diff line change
1
+ # frozen_string_literal: true
2
+
3
+ module HasMany
4
+ class Address < ApplicationRecord
5
+ has_many :phones , class_name : 'HasMany::Phone'
6
+ end
7
+ end
Original file line number Diff line number Diff line change
1
+ # frozen_string_literal: true
2
+
3
+ module HasMany
4
+ class Phone < ApplicationRecord
5
+ end
6
+ end
Original file line number Diff line number Diff line change
1
+ # frozen_string_literal: true
2
+
3
+ module HasMany
4
+ class User < ApplicationRecord
5
+ has_many :addresses , class_name : 'HasMany::Address'
6
+ end
7
+ end
Original file line number Diff line number Diff line change
1
+ # frozen_string_literal: true
2
+
3
+ class CreateHasManyTables < ActiveRecord ::Migration [ 5.2 ]
4
+ def change
5
+ create_table :has_many_users , &:timestamps
6
+
7
+ create_table :has_many_addresses do |t |
8
+ t . references :user , foreign_key : { to_table : :has_many_users }
9
+ t . text :data
10
+
11
+ t . timestamps
12
+ end
13
+
14
+ create_table :has_many_phone do |t |
15
+ t . references :address , foreign_key : false
16
+ end
17
+ end
18
+ end
Original file line number Diff line number Diff line change
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec . describe Dbcop ::HasMany ::ForeignKey do
6
+ describe '#call' do
7
+ context 'when there is fereign key to the parent table' do
8
+ let ( :result ) do
9
+ described_class . new ( HasMany ::User ) . call
10
+ end
11
+
12
+ it { expect ( result ) . to be_truthy }
13
+ end
14
+
15
+ context 'when there is no foreign key to the parent table' do
16
+ let ( :result ) { described_class . new ( HasMany ::Address ) . call }
17
+
18
+ it { expect ( result ) . to be_falsey }
19
+ end
20
+ end
21
+ end
0 commit comments