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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +179 -0
  3. data/.gitignore +1 -1
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +7 -0
  6. data/.travis.yml +16 -9
  7. data/Appraisals +19 -12
  8. data/Gemfile +1 -8
  9. data/Gemfile.lock +60 -59
  10. data/README.md +5 -2
  11. data/Rakefile +4 -4
  12. data/bin/lol_dba +2 -2
  13. data/gemfiles/rails_3_2.gemfile +0 -3
  14. data/gemfiles/rails_3_2.gemfile.lock +33 -31
  15. data/gemfiles/rails_4_0.gemfile +0 -3
  16. data/gemfiles/rails_4_0.gemfile.lock +31 -27
  17. data/gemfiles/rails_4_1.gemfile +0 -3
  18. data/gemfiles/rails_4_1.gemfile.lock +29 -26
  19. data/gemfiles/rails_4_2.gemfile +0 -3
  20. data/gemfiles/rails_4_2.gemfile.lock +52 -48
  21. data/gemfiles/rails_5_0.gemfile +0 -3
  22. data/gemfiles/rails_5_0.gemfile.lock +55 -51
  23. data/gemfiles/rails_5_1.gemfile +0 -3
  24. data/gemfiles/rails_5_1.gemfile.lock +57 -53
  25. data/gemfiles/rails_5_2.gemfile +9 -0
  26. data/gemfiles/rails_5_2.gemfile.lock +115 -0
  27. data/lib/lol_dba/cli.rb +26 -15
  28. data/lib/lol_dba/index_finding/belongs_to.rb +32 -0
  29. data/lib/lol_dba/index_finding/error_logging.rb +15 -0
  30. data/lib/lol_dba/index_finding/has_and_belongs_to_many.rb +20 -0
  31. data/lib/lol_dba/index_finding/has_many.rb +47 -0
  32. data/lib/lol_dba/index_finding/index_finder.rb +81 -0
  33. data/lib/lol_dba/index_finding/migration_formatter.rb +50 -0
  34. data/lib/lol_dba/index_finding/relation_inspector.rb +30 -0
  35. data/lib/lol_dba/index_finding/relation_inspector_factory.rb +13 -0
  36. data/lib/lol_dba/rails_compatibility.rb +47 -0
  37. data/lib/lol_dba/railtie.rb +2 -2
  38. data/lib/lol_dba/sql_migrations/migration.rb +51 -0
  39. data/lib/lol_dba/sql_migrations/migration_mocker.rb +70 -0
  40. data/lib/lol_dba/sql_migrations/sql_generator.rb +49 -0
  41. data/lib/lol_dba/sql_migrations/writer.rb +29 -0
  42. data/lib/lol_dba/version.rb +1 -1
  43. data/lib/lol_dba.rb +14 -188
  44. data/lib/tasks/lol_dba.rake +7 -8
  45. data/lol_dba.gemspec +19 -16
  46. data/spec/associations_index_spec.rb +43 -56
  47. data/spec/fixtures/app/models/address.rb +0 -1
  48. data/spec/fixtures/app/models/billable_week.rb +1 -3
  49. data/spec/fixtures/app/models/company.rb +0 -1
  50. data/spec/fixtures/app/models/complex_billable_week.rb +0 -1
  51. data/spec/fixtures/app/models/complex_timesheet.rb +0 -1
  52. data/spec/fixtures/app/models/country.rb +1 -1
  53. data/spec/fixtures/app/models/favourite.rb +0 -2
  54. data/spec/fixtures/app/models/freelancer.rb +1 -3
  55. data/spec/fixtures/app/models/gift.rb +0 -2
  56. data/spec/fixtures/app/models/god.rb +1 -1
  57. data/spec/fixtures/app/models/project.rb +0 -2
  58. data/spec/fixtures/app/models/timesheet.rb +1 -3
  59. data/spec/fixtures/app/models/user.rb +0 -2
  60. data/spec/fixtures/app/models/worker.rb +0 -1
  61. data/spec/fixtures/app/models/worker_user.rb +2 -3
  62. data/spec/fixtures/schema.rb +53 -53
  63. data/spec/migration_formatter_spec.rb +66 -0
  64. data/spec/spec_helper.rb +17 -9
  65. data/spec/sql_generator_spec.rb +9 -0
  66. metadata +88 -19
  67. data/lib/lol_dba/migration.rb +0 -40
  68. data/lib/lol_dba/sql_generator.rb +0 -96
  69. data/lib/lol_dba/writer.rb +0 -25
  70. 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.4)
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.5)
13
- actionview (= 5.1.5)
14
- activesupport (= 5.1.5)
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.5)
20
- activesupport (= 5.1.5)
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.5)
26
- activesupport (= 5.1.5)
27
- activerecord (5.1.5)
28
- activemodel (= 5.1.5)
29
- activesupport (= 5.1.5)
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.5)
31
+ activesupport (5.1.7)
32
32
  concurrent-ruby (~> 1.0, >= 1.0.2)
33
- i18n (~> 0.7)
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.0.5)
43
- crass (1.0.3)
42
+ concurrent-ruby (1.1.5)
43
+ crass (1.0.4)
44
44
  diff-lcs (1.3)
45
- erubi (1.7.0)
46
- i18n (0.9.5)
45
+ docile (1.3.1)
46
+ erubi (1.8.0)
47
+ i18n (1.6.0)
47
48
  concurrent-ruby (~> 1.0)
48
- loofah (2.2.0)
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.0)
52
- mini_portile2 (2.3.0)
53
+ method_source (0.9.2)
54
+ mini_portile2 (2.4.0)
53
55
  minitest (5.11.3)
54
- nokogiri (1.8.2)
55
- mini_portile2 (~> 2.3.0)
56
- power_assert (1.1.1)
57
- rack (2.0.4)
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.3)
64
- loofah (~> 2.0)
65
- railties (5.1.5)
66
- actionpack (= 5.1.5)
67
- activesupport (= 5.1.5)
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.0)
72
- rspec-core (3.7.1)
73
- rspec-support (~> 3.7.0)
74
- rspec-expectations (3.7.0)
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.7.0)
77
- rspec-mocks (3.7.0)
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.7.0)
80
- rspec-rails (3.7.2)
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.7.0)
85
- rspec-expectations (~> 3.7.0)
86
- rspec-mocks (~> 3.7.0)
87
- rspec-support (~> 3.7.0)
88
- rspec-support (3.7.1)
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
- test-unit (3.2.7)
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
- sqlite3
108
- test-unit
111
+ simplecov (~> 0.1)
112
+ sqlite3 (~> 1.3.5)
109
113
 
110
114
  BUNDLED WITH
111
- 1.16.1
115
+ 1.17.3
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 5.2.0"
6
+ gem "actionpack", "~> 5.2.0"
7
+ gem "railties", "~> 5.2.0"
8
+
9
+ gemspec path: "../"
@@ -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', 'Show stack traces when an error occurs.') { |v| options[:debug] = v }
13
- opts.on_tail("-v", "--version", "Show version") do
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, @options = path, options
28
+ @path = path
29
+ @options = options
24
30
  end
25
31
 
26
- def start
32
+ def start(arg)
27
33
  load_application
28
- arg = ARGV.first
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.simple_migration
46
+ LolDba::IndexFinder.run
31
47
  elsif arg !~ /\[/
32
- LolDba::SqlGenerator.generate("all")
48
+ LolDba::SqlGenerator.run('all')
33
49
  else
34
50
  which = arg.match(/.*\[(.*)\].*/).captures[0]
35
- LolDba::SqlGenerator.generate(which)
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
@@ -0,0 +1,13 @@
1
+ module LolDba
2
+ class RelationInspectorFactory
3
+ TYPES = {
4
+ belongs_to: BelongsTo,
5
+ has_and_belongs_to_many: HasAndBelongsToMany,
6
+ has_many: HasMany
7
+ }.freeze
8
+
9
+ def self.for(type)
10
+ TYPES[type]
11
+ end
12
+ end
13
+ end