lol_dba 2.1.5 → 2.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.codeclimate.yml +179 -0
- data/.gitignore +1 -1
- data/.rspec +2 -0
- data/.rubocop.yml +7 -0
- data/.travis.yml +16 -9
- data/Appraisals +19 -12
- data/Gemfile +1 -8
- data/Gemfile.lock +60 -59
- data/README.md +5 -2
- data/Rakefile +4 -4
- data/bin/lol_dba +2 -2
- data/gemfiles/rails_3_2.gemfile +0 -3
- data/gemfiles/rails_3_2.gemfile.lock +33 -31
- data/gemfiles/rails_4_0.gemfile +0 -3
- data/gemfiles/rails_4_0.gemfile.lock +31 -27
- data/gemfiles/rails_4_1.gemfile +0 -3
- data/gemfiles/rails_4_1.gemfile.lock +29 -26
- data/gemfiles/rails_4_2.gemfile +0 -3
- data/gemfiles/rails_4_2.gemfile.lock +52 -48
- data/gemfiles/rails_5_0.gemfile +0 -3
- data/gemfiles/rails_5_0.gemfile.lock +55 -51
- data/gemfiles/rails_5_1.gemfile +0 -3
- data/gemfiles/rails_5_1.gemfile.lock +57 -53
- data/gemfiles/rails_5_2.gemfile +9 -0
- data/gemfiles/rails_5_2.gemfile.lock +115 -0
- data/lib/lol_dba/cli.rb +26 -15
- data/lib/lol_dba/index_finding/belongs_to.rb +32 -0
- data/lib/lol_dba/index_finding/error_logging.rb +15 -0
- data/lib/lol_dba/index_finding/has_and_belongs_to_many.rb +20 -0
- data/lib/lol_dba/index_finding/has_many.rb +47 -0
- data/lib/lol_dba/index_finding/index_finder.rb +81 -0
- data/lib/lol_dba/index_finding/migration_formatter.rb +50 -0
- data/lib/lol_dba/index_finding/relation_inspector.rb +30 -0
- data/lib/lol_dba/index_finding/relation_inspector_factory.rb +13 -0
- data/lib/lol_dba/rails_compatibility.rb +47 -0
- data/lib/lol_dba/railtie.rb +2 -2
- data/lib/lol_dba/sql_migrations/migration.rb +51 -0
- data/lib/lol_dba/sql_migrations/migration_mocker.rb +70 -0
- data/lib/lol_dba/sql_migrations/sql_generator.rb +49 -0
- data/lib/lol_dba/sql_migrations/writer.rb +29 -0
- data/lib/lol_dba/version.rb +1 -1
- data/lib/lol_dba.rb +14 -188
- data/lib/tasks/lol_dba.rake +7 -8
- data/lol_dba.gemspec +19 -16
- data/spec/associations_index_spec.rb +43 -56
- data/spec/fixtures/app/models/address.rb +0 -1
- data/spec/fixtures/app/models/billable_week.rb +1 -3
- data/spec/fixtures/app/models/company.rb +0 -1
- data/spec/fixtures/app/models/complex_billable_week.rb +0 -1
- data/spec/fixtures/app/models/complex_timesheet.rb +0 -1
- data/spec/fixtures/app/models/country.rb +1 -1
- data/spec/fixtures/app/models/favourite.rb +0 -2
- data/spec/fixtures/app/models/freelancer.rb +1 -3
- data/spec/fixtures/app/models/gift.rb +0 -2
- data/spec/fixtures/app/models/god.rb +1 -1
- data/spec/fixtures/app/models/project.rb +0 -2
- data/spec/fixtures/app/models/timesheet.rb +1 -3
- data/spec/fixtures/app/models/user.rb +0 -2
- data/spec/fixtures/app/models/worker.rb +0 -1
- data/spec/fixtures/app/models/worker_user.rb +2 -3
- data/spec/fixtures/schema.rb +53 -53
- data/spec/migration_formatter_spec.rb +66 -0
- data/spec/spec_helper.rb +17 -9
- data/spec/sql_generator_spec.rb +9 -0
- metadata +88 -19
- data/lib/lol_dba/migration.rb +0 -40
- data/lib/lol_dba/sql_generator.rb +0 -96
- data/lib/lol_dba/writer.rb +0 -25
- data/spec/common_function_spec.rb +0 -96
@@ -1,36 +1,36 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ..
|
3
3
|
specs:
|
4
|
-
lol_dba (2.1.
|
5
|
-
actionpack (>= 3.0)
|
6
|
-
activerecord (>= 3.0)
|
7
|
-
railties (>= 3.0)
|
4
|
+
lol_dba (2.1.5)
|
5
|
+
actionpack (>= 3.0, < 6.0)
|
6
|
+
activerecord (>= 3.0, < 6.0)
|
7
|
+
railties (>= 3.0, < 6.0)
|
8
8
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
|
-
actionpack (5.1.
|
13
|
-
actionview (= 5.1.
|
14
|
-
activesupport (= 5.1.
|
12
|
+
actionpack (5.1.7)
|
13
|
+
actionview (= 5.1.7)
|
14
|
+
activesupport (= 5.1.7)
|
15
15
|
rack (~> 2.0)
|
16
16
|
rack-test (>= 0.6.3)
|
17
17
|
rails-dom-testing (~> 2.0)
|
18
18
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
19
|
-
actionview (5.1.
|
20
|
-
activesupport (= 5.1.
|
19
|
+
actionview (5.1.7)
|
20
|
+
activesupport (= 5.1.7)
|
21
21
|
builder (~> 3.1)
|
22
22
|
erubi (~> 1.4)
|
23
23
|
rails-dom-testing (~> 2.0)
|
24
24
|
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
25
|
-
activemodel (5.1.
|
26
|
-
activesupport (= 5.1.
|
27
|
-
activerecord (5.1.
|
28
|
-
activemodel (= 5.1.
|
29
|
-
activesupport (= 5.1.
|
25
|
+
activemodel (5.1.7)
|
26
|
+
activesupport (= 5.1.7)
|
27
|
+
activerecord (5.1.7)
|
28
|
+
activemodel (= 5.1.7)
|
29
|
+
activesupport (= 5.1.7)
|
30
30
|
arel (~> 8.0)
|
31
|
-
activesupport (5.1.
|
31
|
+
activesupport (5.1.7)
|
32
32
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
33
|
-
i18n (
|
33
|
+
i18n (>= 0.7, < 2)
|
34
34
|
minitest (~> 5.1)
|
35
35
|
tzinfo (~> 1.1)
|
36
36
|
appraisal (2.2.0)
|
@@ -39,57 +39,61 @@ GEM
|
|
39
39
|
thor (>= 0.14.0)
|
40
40
|
arel (8.0.0)
|
41
41
|
builder (3.2.3)
|
42
|
-
concurrent-ruby (1.
|
43
|
-
crass (1.0.
|
42
|
+
concurrent-ruby (1.1.5)
|
43
|
+
crass (1.0.4)
|
44
44
|
diff-lcs (1.3)
|
45
|
-
|
46
|
-
|
45
|
+
docile (1.3.1)
|
46
|
+
erubi (1.8.0)
|
47
|
+
i18n (1.6.0)
|
47
48
|
concurrent-ruby (~> 1.0)
|
48
|
-
|
49
|
+
json (2.2.0)
|
50
|
+
loofah (2.2.3)
|
49
51
|
crass (~> 1.0.2)
|
50
52
|
nokogiri (>= 1.5.9)
|
51
|
-
method_source (0.9.
|
52
|
-
mini_portile2 (2.
|
53
|
+
method_source (0.9.2)
|
54
|
+
mini_portile2 (2.4.0)
|
53
55
|
minitest (5.11.3)
|
54
|
-
nokogiri (1.
|
55
|
-
mini_portile2 (~> 2.
|
56
|
-
|
57
|
-
rack (
|
58
|
-
rack-test (0.8.3)
|
56
|
+
nokogiri (1.10.2)
|
57
|
+
mini_portile2 (~> 2.4.0)
|
58
|
+
rack (2.0.7)
|
59
|
+
rack-test (1.1.0)
|
59
60
|
rack (>= 1.0, < 3)
|
60
61
|
rails-dom-testing (2.0.3)
|
61
62
|
activesupport (>= 4.2.0)
|
62
63
|
nokogiri (>= 1.6)
|
63
|
-
rails-html-sanitizer (1.0.
|
64
|
-
loofah (~> 2.
|
65
|
-
railties (5.1.
|
66
|
-
actionpack (= 5.1.
|
67
|
-
activesupport (= 5.1.
|
64
|
+
rails-html-sanitizer (1.0.4)
|
65
|
+
loofah (~> 2.2, >= 2.2.2)
|
66
|
+
railties (5.1.7)
|
67
|
+
actionpack (= 5.1.7)
|
68
|
+
activesupport (= 5.1.7)
|
68
69
|
method_source
|
69
70
|
rake (>= 0.8.7)
|
70
71
|
thor (>= 0.18.1, < 2.0)
|
71
|
-
rake (12.3.
|
72
|
-
rspec-core (3.
|
73
|
-
rspec-support (~> 3.
|
74
|
-
rspec-expectations (3.
|
72
|
+
rake (12.3.2)
|
73
|
+
rspec-core (3.8.0)
|
74
|
+
rspec-support (~> 3.8.0)
|
75
|
+
rspec-expectations (3.8.2)
|
75
76
|
diff-lcs (>= 1.2.0, < 2.0)
|
76
|
-
rspec-support (~> 3.
|
77
|
-
rspec-mocks (3.
|
77
|
+
rspec-support (~> 3.8.0)
|
78
|
+
rspec-mocks (3.8.0)
|
78
79
|
diff-lcs (>= 1.2.0, < 2.0)
|
79
|
-
rspec-support (~> 3.
|
80
|
-
rspec-rails (3.
|
80
|
+
rspec-support (~> 3.8.0)
|
81
|
+
rspec-rails (3.8.2)
|
81
82
|
actionpack (>= 3.0)
|
82
83
|
activesupport (>= 3.0)
|
83
84
|
railties (>= 3.0)
|
84
|
-
rspec-core (~> 3.
|
85
|
-
rspec-expectations (~> 3.
|
86
|
-
rspec-mocks (~> 3.
|
87
|
-
rspec-support (~> 3.
|
88
|
-
rspec-support (3.
|
85
|
+
rspec-core (~> 3.8.0)
|
86
|
+
rspec-expectations (~> 3.8.0)
|
87
|
+
rspec-mocks (~> 3.8.0)
|
88
|
+
rspec-support (~> 3.8.0)
|
89
|
+
rspec-support (3.8.0)
|
90
|
+
simplecov (0.16.1)
|
91
|
+
docile (~> 1.1)
|
92
|
+
json (>= 1.8, < 3)
|
93
|
+
simplecov-html (~> 0.10.0)
|
94
|
+
simplecov-html (0.10.2)
|
89
95
|
sqlite3 (1.3.13)
|
90
|
-
|
91
|
-
power_assert
|
92
|
-
thor (0.20.0)
|
96
|
+
thor (0.20.3)
|
93
97
|
thread_safe (0.3.6)
|
94
98
|
tzinfo (1.2.5)
|
95
99
|
thread_safe (~> 0.1)
|
@@ -100,12 +104,12 @@ PLATFORMS
|
|
100
104
|
DEPENDENCIES
|
101
105
|
actionpack (~> 5.1.0)
|
102
106
|
activerecord (~> 5.1.0)
|
103
|
-
appraisal
|
107
|
+
appraisal (~> 2.2)
|
104
108
|
lol_dba!
|
105
109
|
railties (~> 5.1.0)
|
106
110
|
rspec-rails
|
107
|
-
|
108
|
-
|
111
|
+
simplecov (~> 0.1)
|
112
|
+
sqlite3 (~> 1.3.5)
|
109
113
|
|
110
114
|
BUNDLED WITH
|
111
|
-
1.
|
115
|
+
1.17.3
|
@@ -0,0 +1,115 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ..
|
3
|
+
specs:
|
4
|
+
lol_dba (2.1.5)
|
5
|
+
actionpack (>= 3.0, < 6.0)
|
6
|
+
activerecord (>= 3.0, < 6.0)
|
7
|
+
railties (>= 3.0, < 6.0)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
actionpack (5.2.3)
|
13
|
+
actionview (= 5.2.3)
|
14
|
+
activesupport (= 5.2.3)
|
15
|
+
rack (~> 2.0)
|
16
|
+
rack-test (>= 0.6.3)
|
17
|
+
rails-dom-testing (~> 2.0)
|
18
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
19
|
+
actionview (5.2.3)
|
20
|
+
activesupport (= 5.2.3)
|
21
|
+
builder (~> 3.1)
|
22
|
+
erubi (~> 1.4)
|
23
|
+
rails-dom-testing (~> 2.0)
|
24
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
25
|
+
activemodel (5.2.3)
|
26
|
+
activesupport (= 5.2.3)
|
27
|
+
activerecord (5.2.3)
|
28
|
+
activemodel (= 5.2.3)
|
29
|
+
activesupport (= 5.2.3)
|
30
|
+
arel (>= 9.0)
|
31
|
+
activesupport (5.2.3)
|
32
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
33
|
+
i18n (>= 0.7, < 2)
|
34
|
+
minitest (~> 5.1)
|
35
|
+
tzinfo (~> 1.1)
|
36
|
+
appraisal (2.2.0)
|
37
|
+
bundler
|
38
|
+
rake
|
39
|
+
thor (>= 0.14.0)
|
40
|
+
arel (9.0.0)
|
41
|
+
builder (3.2.3)
|
42
|
+
concurrent-ruby (1.1.5)
|
43
|
+
crass (1.0.4)
|
44
|
+
diff-lcs (1.3)
|
45
|
+
docile (1.3.1)
|
46
|
+
erubi (1.8.0)
|
47
|
+
i18n (1.6.0)
|
48
|
+
concurrent-ruby (~> 1.0)
|
49
|
+
json (2.2.0)
|
50
|
+
loofah (2.2.3)
|
51
|
+
crass (~> 1.0.2)
|
52
|
+
nokogiri (>= 1.5.9)
|
53
|
+
method_source (0.9.2)
|
54
|
+
mini_portile2 (2.4.0)
|
55
|
+
minitest (5.11.3)
|
56
|
+
nokogiri (1.10.2)
|
57
|
+
mini_portile2 (~> 2.4.0)
|
58
|
+
rack (2.0.7)
|
59
|
+
rack-test (1.1.0)
|
60
|
+
rack (>= 1.0, < 3)
|
61
|
+
rails-dom-testing (2.0.3)
|
62
|
+
activesupport (>= 4.2.0)
|
63
|
+
nokogiri (>= 1.6)
|
64
|
+
rails-html-sanitizer (1.0.4)
|
65
|
+
loofah (~> 2.2, >= 2.2.2)
|
66
|
+
railties (5.2.3)
|
67
|
+
actionpack (= 5.2.3)
|
68
|
+
activesupport (= 5.2.3)
|
69
|
+
method_source
|
70
|
+
rake (>= 0.8.7)
|
71
|
+
thor (>= 0.19.0, < 2.0)
|
72
|
+
rake (12.3.2)
|
73
|
+
rspec-core (3.8.0)
|
74
|
+
rspec-support (~> 3.8.0)
|
75
|
+
rspec-expectations (3.8.2)
|
76
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
77
|
+
rspec-support (~> 3.8.0)
|
78
|
+
rspec-mocks (3.8.0)
|
79
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
80
|
+
rspec-support (~> 3.8.0)
|
81
|
+
rspec-rails (3.8.2)
|
82
|
+
actionpack (>= 3.0)
|
83
|
+
activesupport (>= 3.0)
|
84
|
+
railties (>= 3.0)
|
85
|
+
rspec-core (~> 3.8.0)
|
86
|
+
rspec-expectations (~> 3.8.0)
|
87
|
+
rspec-mocks (~> 3.8.0)
|
88
|
+
rspec-support (~> 3.8.0)
|
89
|
+
rspec-support (3.8.0)
|
90
|
+
simplecov (0.16.1)
|
91
|
+
docile (~> 1.1)
|
92
|
+
json (>= 1.8, < 3)
|
93
|
+
simplecov-html (~> 0.10.0)
|
94
|
+
simplecov-html (0.10.2)
|
95
|
+
sqlite3 (1.3.13)
|
96
|
+
thor (0.20.3)
|
97
|
+
thread_safe (0.3.6)
|
98
|
+
tzinfo (1.2.5)
|
99
|
+
thread_safe (~> 0.1)
|
100
|
+
|
101
|
+
PLATFORMS
|
102
|
+
ruby
|
103
|
+
|
104
|
+
DEPENDENCIES
|
105
|
+
actionpack (~> 5.2.0)
|
106
|
+
activerecord (~> 5.2.0)
|
107
|
+
appraisal (~> 2.2)
|
108
|
+
lol_dba!
|
109
|
+
railties (~> 5.2.0)
|
110
|
+
rspec-rails
|
111
|
+
simplecov (~> 0.1)
|
112
|
+
sqlite3 (~> 1.3.5)
|
113
|
+
|
114
|
+
BUNDLED WITH
|
115
|
+
1.17.3
|
data/lib/lol_dba/cli.rb
CHANGED
@@ -4,43 +4,54 @@ require 'lol_dba/version'
|
|
4
4
|
|
5
5
|
module LolDba
|
6
6
|
class CLI
|
7
|
-
|
8
7
|
class << self
|
9
8
|
def start
|
9
|
+
new(Dir.pwd, parse_options).start(ARGV.first)
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse_options
|
10
13
|
options = {}
|
11
14
|
OptionParser.new do |opts|
|
12
|
-
opts.on('-d', '--debug',
|
13
|
-
|
15
|
+
opts.on('-d', '--debug',
|
16
|
+
'Show stack traces when an error occurs.') do |opt|
|
17
|
+
options[:debug] = opt
|
18
|
+
end
|
19
|
+
opts.on_tail('-v', '--version', 'Show version') do
|
14
20
|
puts LolDba::VERSION
|
15
21
|
exit
|
16
22
|
end
|
17
23
|
end.parse!
|
18
|
-
new(Dir.pwd, options).start
|
19
24
|
end
|
20
25
|
end
|
21
26
|
|
22
27
|
def initialize(path, options)
|
23
|
-
@path
|
28
|
+
@path = path
|
29
|
+
@options = options
|
24
30
|
end
|
25
31
|
|
26
|
-
def start
|
32
|
+
def start(arg)
|
27
33
|
load_application
|
28
|
-
arg
|
34
|
+
select_action(arg)
|
35
|
+
rescue Exception => exception
|
36
|
+
if @options[:debug]
|
37
|
+
warn "Failed: #{exception.class}: #{exception.message}"
|
38
|
+
warn exception.backtrace.map { |trace| " from #{trace}" }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def select_action(arg)
|
29
45
|
if arg =~ /db:find_indexes/
|
30
|
-
LolDba.
|
46
|
+
LolDba::IndexFinder.run
|
31
47
|
elsif arg !~ /\[/
|
32
|
-
LolDba::SqlGenerator.
|
48
|
+
LolDba::SqlGenerator.run('all')
|
33
49
|
else
|
34
50
|
which = arg.match(/.*\[(.*)\].*/).captures[0]
|
35
|
-
LolDba::SqlGenerator.
|
51
|
+
LolDba::SqlGenerator.run(which)
|
36
52
|
end
|
37
|
-
rescue Exception => e
|
38
|
-
$stderr.puts "Failed: #{e.class}: #{e.message}" if @options[:debug]
|
39
|
-
$stderr.puts e.backtrace.map { |t| " from #{t}" } if @options[:debug]
|
40
53
|
end
|
41
54
|
|
42
|
-
protected
|
43
|
-
|
44
55
|
# Tks to https://github.com/voormedia/rails-erd/blob/master/lib/rails_erd/cli.rb
|
45
56
|
def load_application
|
46
57
|
require "#{@path}/config/environment"
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module LolDba
|
2
|
+
class BelongsTo < RelationInspector
|
3
|
+
def relation_columns
|
4
|
+
if reflection_options.options[:polymorphic]
|
5
|
+
name = reflection_options.name
|
6
|
+
poly_type = "#{name}_type"
|
7
|
+
poly_id = "#{name}_id"
|
8
|
+
[poly_type, poly_id].sort
|
9
|
+
else
|
10
|
+
foreign_key = non_polymorphic_fk
|
11
|
+
|
12
|
+
# not a clue why rails 4.1+ creates this left_side_id thing
|
13
|
+
foreign_key == 'left_side_id' ? nil : foreign_key.to_s
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def table_name
|
18
|
+
model_class.table_name
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def non_polymorphic_fk
|
24
|
+
foreign_key = reflection_options.options[:foreign_key]
|
25
|
+
foreign_key ||= if reflection_options.respond_to?(:primary_key_name)
|
26
|
+
reflection_options.primary_key_name
|
27
|
+
else
|
28
|
+
reflection_options.foreign_key
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module LolDba
|
2
|
+
class ErrorLogging
|
3
|
+
def self.log(model_class, reflection_options, exception)
|
4
|
+
puts 'some errors here...'
|
5
|
+
puts 'please, create an issue with the following information here:'
|
6
|
+
puts 'https://github.com/plentz/lol_dba/issues'
|
7
|
+
puts '***************************'
|
8
|
+
puts "class: #{model_class}"
|
9
|
+
puts "association type: #{reflection_options.macro}"
|
10
|
+
puts "association options: #{reflection_options.options}"
|
11
|
+
puts "exception: #{exception.message}"
|
12
|
+
exception.backtrace.each { |trace| puts trace }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module LolDba
|
2
|
+
class HasAndBelongsToMany < RelationInspector
|
3
|
+
def relation_columns
|
4
|
+
foreign_key = get_through_foreign_key(model_class, reflection_options)
|
5
|
+
index_name = [association_fk, foreign_key].map(&:to_s).sort
|
6
|
+
end
|
7
|
+
|
8
|
+
def table_name
|
9
|
+
table_name = reflection_options.options[:join_table]
|
10
|
+
table_name || [model_class.table_name, reflection_name.to_s].sort.join('_')
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def association_fk
|
16
|
+
association_fk = reflection_options.options[:association_foreign_key]
|
17
|
+
association_fk || "#{reflection_name.to_s.singularize}_id"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module LolDba
|
2
|
+
class HasMany < RelationInspector
|
3
|
+
def relation_columns
|
4
|
+
# has_many tables are threaten by the other side of the relation
|
5
|
+
return nil unless through && reflections[through.to_s]
|
6
|
+
|
7
|
+
# FIXME: currently we don't support :through =>
|
8
|
+
# :another_regular_has_many_and_non_through_relation
|
9
|
+
if (association_foreign_key = find_association_fk).present?
|
10
|
+
[association_foreign_key, foreign_key].map(&:to_s).sort
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def table_name
|
15
|
+
through_class.table_name
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def find_association_fk
|
21
|
+
if (source = reflection_options.options[:source])
|
22
|
+
association_reflection = through_reflections[source.to_s]
|
23
|
+
return nil if association_reflection.options[:polymorphic]
|
24
|
+
get_through_foreign_key(association_reflection.klass, reflection_options)
|
25
|
+
elsif belongs_to_reflections = through_reflections[reflection_name.singularize]
|
26
|
+
# go to joining model through has_many and find belongs_to
|
27
|
+
belongs_to_reflections.options[:foreign_key]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def through_class
|
32
|
+
reflections[through.to_s].klass
|
33
|
+
end
|
34
|
+
|
35
|
+
def through
|
36
|
+
reflection_options.options[:through]
|
37
|
+
end
|
38
|
+
|
39
|
+
def foreign_key
|
40
|
+
get_through_foreign_key(model_class, reflection_options)
|
41
|
+
end
|
42
|
+
|
43
|
+
def through_reflections
|
44
|
+
through_class.reflections.stringify_keys
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module LolDba
|
2
|
+
class IndexFinder
|
3
|
+
def self.run
|
4
|
+
MigrationFormatter.new(check_for_indexes).puts_migration_content
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.check_for_indexes
|
8
|
+
eager_load_if_needed
|
9
|
+
|
10
|
+
required_indexes = Hash.new([])
|
11
|
+
|
12
|
+
model_classes.each do |model_class|
|
13
|
+
unless model_class.descends_from_active_record?
|
14
|
+
index_name = [model_class.inheritance_column, model_class.base_class.primary_key].sort
|
15
|
+
required_indexes[model_class.base_class.table_name] += [index_name]
|
16
|
+
end
|
17
|
+
reflections = model_class.reflections.stringify_keys
|
18
|
+
reflections.each_pair do |reflection_name, reflection_options|
|
19
|
+
begin
|
20
|
+
clazz = RelationInspectorFactory.for(reflection_options.macro)
|
21
|
+
next unless clazz.present?
|
22
|
+
inspector = clazz.new(model_class, reflection_options,
|
23
|
+
reflection_name)
|
24
|
+
columns = inspector.relation_columns
|
25
|
+
|
26
|
+
unless columns.nil? || reflection_options.options.include?(:class)
|
27
|
+
required_indexes[inspector.table_name.to_s] += [columns]
|
28
|
+
end
|
29
|
+
rescue StandardError => exception
|
30
|
+
LolDba::ErrorLogging.log(model_class, reflection_options, exception)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
missing_indexes(required_indexes)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.missing_indexes(indexes_required)
|
39
|
+
missing_indexes = {}
|
40
|
+
indexes_required.each do |table_name, foreign_keys|
|
41
|
+
next if foreign_keys.blank? || !tables.include?(table_name.to_s)
|
42
|
+
keys_to_add = foreign_keys.uniq - existing_indexes(table_name)
|
43
|
+
missing_indexes[table_name] = keys_to_add unless keys_to_add.empty?
|
44
|
+
end
|
45
|
+
missing_indexes
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.tables
|
49
|
+
LolDba::RailsCompatibility.tables
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.existing_indexes(table_name)
|
53
|
+
table_indexes(table_name) + primary_key(table_name)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.table_indexes(table_name)
|
57
|
+
indexes = ActiveRecord::Base.connection.indexes(table_name.to_sym)
|
58
|
+
indexes.collect do |index|
|
59
|
+
index.columns.size > 1 ? index.columns : index.columns.first
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.primary_key(table_name)
|
64
|
+
Array(ActiveRecord::Base.connection.primary_key(table_name.to_s))
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.model_classes
|
68
|
+
ActiveRecord::Base.descendants.select do |obj|
|
69
|
+
Class == obj.class && session_store?(obj)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.session_store?(obj)
|
74
|
+
!defined?(ActiveRecord::SessionStore::Session) || obj != ActiveRecord::SessionStore::Session
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.eager_load_if_needed
|
78
|
+
Rails.application.eager_load! if defined?(Rails) && !Rails.env.test?
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module LolDba
|
2
|
+
class MigrationFormatter
|
3
|
+
def initialize(indexes)
|
4
|
+
@indexes = indexes
|
5
|
+
end
|
6
|
+
|
7
|
+
def puts_migration_content
|
8
|
+
formated_indexes = format_for_migration(@indexes)
|
9
|
+
if formated_indexes.blank?
|
10
|
+
puts 'Yey, no missing indexes found!'
|
11
|
+
else
|
12
|
+
puts migration_instructions(formated_indexes)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def format_for_migration(missing_indexes)
|
17
|
+
add = []
|
18
|
+
missing_indexes.each do |table_name, keys_to_add|
|
19
|
+
keys_to_add.each do |key|
|
20
|
+
next if key.blank?
|
21
|
+
add << format_index(table_name, key)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
add
|
25
|
+
end
|
26
|
+
|
27
|
+
def format_index(table_name, key)
|
28
|
+
if key.is_a?(Array)
|
29
|
+
keys = key.collect { |col| ":#{col}" }
|
30
|
+
"add_index :#{table_name}, [#{keys.join(', ')}]"
|
31
|
+
else
|
32
|
+
"add_index :#{table_name}, :#{key}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def migration_instructions(formated_indexes)
|
37
|
+
<<-MIGRATION
|
38
|
+
* TIP: if you have a problem with the index name('index name too long'), you can
|
39
|
+
solve with the :name option. Something like :name => 'my_index'.
|
40
|
+
* run `rails g migration AddMissingIndexes` and add the following content:
|
41
|
+
|
42
|
+
class AddMissingIndexes < ActiveRecord::Migration
|
43
|
+
def change
|
44
|
+
#{formated_indexes.sort.uniq.join("\n ")}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
MIGRATION
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module LolDba
|
2
|
+
class RelationInspector
|
3
|
+
attr_reader :model_class, :reflection_options, :reflection_name
|
4
|
+
|
5
|
+
def initialize(model_class, reflection_options, reflection_name)
|
6
|
+
@model_class = model_class
|
7
|
+
@reflection_options = reflection_options
|
8
|
+
@reflection_name = reflection_name
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_through_foreign_key(target_class, reflection_options)
|
12
|
+
# has_many :through
|
13
|
+
reflection = target_class.reflections[reflection_options.options[:through].to_s]
|
14
|
+
|
15
|
+
# has_and_belongs_to_many
|
16
|
+
reflection ||= reflection_options
|
17
|
+
|
18
|
+
# Guess foreign key?
|
19
|
+
if reflection.options[:foreign_key]
|
20
|
+
reflection.options[:foreign_key]
|
21
|
+
else
|
22
|
+
"#{target_class.name.tableize.singularize}_id"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def reflections
|
27
|
+
model_class.reflections.stringify_keys
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|