immigrant 0.2.0 → 0.3.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 083bc4d18b381bf62146a71c000495a4b108fed2
4
+ data.tar.gz: b096073f024d41aa6362c2d3db3a3014aa8016ba
5
+ SHA512:
6
+ metadata.gz: e1fe3e50d541c96400fbb476bcac1ac9176aa51e19b8d7e373398307aa34c0cabbea3a9353f932dd47d39439558fb367418b82eae651cac1fe7368c0d7d35e79
7
+ data.tar.gz: ce45456a29ebf673ea5a6b987990c70c784387db86804555edebe16812322d31534ff877aae37c071419dae0b26ba6052db2a51b31b5ebd1652f4282b8cbb7a8
@@ -3,7 +3,7 @@ require 'rails/generators/active_record'
3
3
  class ImmigrationGenerator < ActiveRecord::Generators::Base
4
4
  def create_immigration_file
5
5
  Rails.application.eager_load!
6
- @keys, warnings = Immigrant.infer_keys
6
+ @keys, warnings = Immigrant::KeyFinder.new.infer_keys
7
7
  warnings.values.each{ |warning| $stderr.puts "WARNING: #{warning}" }
8
8
  @keys.each do |key|
9
9
  next unless key.options[:dependent] == :delete
@@ -1,11 +1,12 @@
1
- module Immigrant
2
- ForeignKeyDefinition = ::Foreigner::ConnectionAdapters::ForeignKeyDefinition
1
+ require_relative 'foreigner'
3
2
 
3
+ module Immigrant
4
4
  TEMPLATE = 'immigration-pre-3.1.rb.erb'
5
5
  FOREIGN_KEY = :primary_key_name
6
- ON_DELETE = :dependent
7
6
 
8
- def self.qualified_reflection?(reflection, klass)
9
- reflection.options[:conditions].present?
7
+ class KeyFinder
8
+ def qualified_reflection?(reflection, klass)
9
+ reflection.options[:conditions].present?
10
+ end
10
11
  end
11
12
  end
@@ -1,12 +1,13 @@
1
- module Immigrant
2
- ForeignKeyDefinition = ::Foreigner::ConnectionAdapters::ForeignKeyDefinition
1
+ require_relative 'foreigner'
3
2
 
3
+ module Immigrant
4
4
  TEMPLATE = 'immigration.rb.erb'
5
5
  FOREIGN_KEY = :foreign_key
6
- ON_DELETE = :dependent
7
6
 
8
- def self.qualified_reflection?(reflection, klass)
9
- reflection.options[:conditions].present?
7
+ class KeyFinder
8
+ def qualified_reflection?(reflection, klass)
9
+ reflection.options[:conditions].present?
10
+ end
10
11
  end
11
12
  end
12
13
 
@@ -1,22 +1,21 @@
1
- module Immigrant
2
- ForeignKeyDefinition = ::Foreigner::ConnectionAdapters::ForeignKeyDefinition
1
+ require_relative 'foreigner'
3
2
 
3
+ module Immigrant
4
4
  TEMPLATE = 'immigration.rb.erb'
5
5
  FOREIGN_KEY = :foreign_key
6
- ON_DELETE = :dependent
7
6
 
8
- def self.qualified_reflection?(reflection, klass)
9
- scope = reflection.scope
10
- if scope.nil?
11
- false
12
- elsif scope.respond_to?(:options)
13
- scope.options[:where].present?
14
- else
15
- klass.instance_exec(*([nil]*scope.arity), &scope).where_values.present?
7
+ class KeyFinder
8
+ def qualified_reflection?(reflection, klass)
9
+ scope = reflection.scope
10
+ if scope.nil?
11
+ false
12
+ else
13
+ klass.instance_exec(*([nil]*scope.arity), &scope).where_values.present?
14
+ end
15
+ rescue
16
+ # if there's an error evaluating the scope block or whatever, just
17
+ # err on the side of caution and assume there are conditions
18
+ true
16
19
  end
17
- rescue
18
- # if there's an error evaluating the scope block or whatever, just
19
- # err on the side of caution and assume there are conditions
20
- true
21
20
  end
22
21
  end
@@ -1,60 +1,18 @@
1
- require "active_record/connection_adapters/abstract/schema_definitions"
1
+ require_relative 'active_record'
2
2
 
3
3
  module Immigrant
4
- ForeignKeyDefinition = ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition
5
-
6
- TEMPLATE = 'immigration.rb.erb'
7
- FOREIGN_KEY = :foreign_key
8
- ON_DELETE = :on_delete
9
-
10
- def self.qualified_reflection?(reflection, klass)
11
- scope = reflection.scope
12
- if scope.nil?
13
- false
14
- elsif scope.respond_to?(:options)
15
- scope.options[:where].present?
16
- else
17
- klass.instance_exec(*([nil]*scope.arity), &scope).where_values.present?
18
- end
19
- rescue
20
- # if there's an error evaluating the scope block or whatever, just
21
- # err on the side of caution and assume there are conditions
22
- true
23
- end
24
-
25
- module ForeignKeyExtensions
26
- # DRY alert: copied from ActiveRecord::SchemaDumper#foreign_keys
27
- def dump_foreign_key(foreign_key)
28
- parts = [
29
- "add_foreign_key #{remove_prefix_and_suffix(foreign_key.from_table).inspect}",
30
- remove_prefix_and_suffix(foreign_key.to_table).inspect,
31
- ]
32
-
33
- if foreign_key.column != foreign_key_column_for(foreign_key.to_table)
34
- parts << "column: #{foreign_key.column.inspect}"
35
- end
36
-
37
- if foreign_key.custom_primary_key?
38
- parts << "primary_key: #{foreign_key.primary_key.inspect}"
4
+ class KeyFinder
5
+ def qualified_reflection?(reflection, klass)
6
+ scope = reflection.scope
7
+ if scope.nil?
8
+ false
9
+ else
10
+ klass.instance_exec(*([nil]*scope.arity), &scope).where_values.present?
39
11
  end
40
-
41
- if foreign_key.name !~ /^fk_rails_[0-9a-f]{10}$/
42
- parts << "name: #{foreign_key.name.inspect}"
43
- end
44
-
45
- parts << "on_update: #{foreign_key.on_update.inspect}" if foreign_key.on_update
46
- parts << "on_delete: #{foreign_key.on_delete.inspect}" if foreign_key.on_delete
47
-
48
- " #{parts.join(', ')}"
49
- end
50
-
51
-
52
- def remove_prefix_and_suffix(table)
53
- table.gsub(/^(#{ActiveRecord::Base.table_name_prefix})(.+)(#{ActiveRecord::Base.table_name_suffix})$/, "\\2")
54
- end
55
-
56
- def foreign_key_column_for(table_name)
57
- "#{table_name.to_s.singularize}_id"
12
+ rescue
13
+ # if there's an error evaluating the scope block or whatever, just
14
+ # err on the side of caution and assume there are conditions
15
+ true
58
16
  end
59
17
  end
60
18
  end
@@ -0,0 +1 @@
1
+ require_relative 'active_record'
@@ -0,0 +1,44 @@
1
+ require "active_record/connection_adapters/abstract/schema_definitions"
2
+
3
+ module Immigrant
4
+ ForeignKeyDefinition = ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition
5
+
6
+ TEMPLATE = 'immigration.rb.erb'
7
+ FOREIGN_KEY = :foreign_key
8
+
9
+ module ForeignKeyExtensions
10
+ # DRY alert: copied from ActiveRecord::SchemaDumper#foreign_keys
11
+ def dump_foreign_key(foreign_key)
12
+ parts = [
13
+ "add_foreign_key #{remove_prefix_and_suffix(foreign_key.from_table).inspect}",
14
+ remove_prefix_and_suffix(foreign_key.to_table).inspect,
15
+ ]
16
+
17
+ if foreign_key.column != foreign_key_column_for(foreign_key.to_table)
18
+ parts << "column: #{foreign_key.column.inspect}"
19
+ end
20
+
21
+ if foreign_key.custom_primary_key?
22
+ parts << "primary_key: #{foreign_key.primary_key.inspect}"
23
+ end
24
+
25
+ if foreign_key.name !~ /^fk_rails_[0-9a-f]{10}$/
26
+ parts << "name: #{foreign_key.name.inspect}"
27
+ end
28
+
29
+ parts << "on_update: #{foreign_key.on_update.inspect}" if foreign_key.on_update
30
+ parts << "on_delete: #{foreign_key.on_delete.inspect}" if foreign_key.on_delete
31
+
32
+ parts.join(', ')
33
+ end
34
+
35
+
36
+ def remove_prefix_and_suffix(table)
37
+ table.gsub(/^(#{ActiveRecord::Base.table_name_prefix})(.+)(#{ActiveRecord::Base.table_name_suffix})$/, "\\2")
38
+ end
39
+
40
+ def foreign_key_column_for(table_name)
41
+ "#{table_name.to_s.singularize}_id"
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,44 @@
1
+ begin
2
+ require 'foreigner'
3
+ rescue LoadError
4
+ $stderr.puts <<-ERR.strip_heredoc
5
+
6
+ ERROR: immigrant requires the foreigner gem (unless you are on rails 4.2+)
7
+
8
+ To fix this, add the following to your Gemfile:
9
+
10
+ gem "foreigner", "~> 1.2"
11
+
12
+ Or just upgrade rails ;) ... lol, "just"
13
+
14
+ ERR
15
+ exit 1
16
+ end
17
+ Foreigner.load
18
+
19
+ module Immigrant
20
+ ForeignKeyDefinition = ::Foreigner::ConnectionAdapters::ForeignKeyDefinition
21
+
22
+ module ForeignKeyExtensions
23
+ include Foreigner::SchemaDumper::ClassMethods
24
+
25
+ def self.included(klass)
26
+ # ForeignKeyExtensions already overrides initialize; override it
27
+ # some more
28
+ klass.send(:include, Module.new{
29
+ def initialize(from_table, to_table, options)
30
+ options.delete(:on_update)
31
+ options[:dependent] = normalize_dependent(options.delete(:on_delete))
32
+ super
33
+ end
34
+ })
35
+ end
36
+
37
+ def normalize_dependent(value)
38
+ case value
39
+ when :cascade then :delete
40
+ else value
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,57 +1,13 @@
1
1
  version = ActiveRecord::VERSION::STRING
2
2
 
3
- if version >= '4.2.'
3
+ if version >= '5.0.'
4
+ require_relative 'compat/5.0'
5
+ elsif version >= '4.2.'
4
6
  require_relative 'compat/4.2'
7
+ elsif version >= '4.0'
8
+ require_relative 'compat/4.0'
9
+ elsif version >= '3.1'
10
+ require_relative 'compat/3.1'
5
11
  else
6
- begin
7
- require 'foreigner'
8
- rescue LoadError
9
- $stderr.puts <<-ERR.strip_heredoc
10
-
11
- ERROR: immigrant requires the foreigner gem (unless you are on rails 4.2+)
12
-
13
- To fix this, add the following to your Gemfile:
14
-
15
- gem "foreigner", "~> 1.2"
16
-
17
- Or just upgrade rails ;) ... lol, "just"
18
-
19
- ERR
20
- exit 1
21
- end
22
- Foreigner.load
23
-
24
- require_relative 'compat/foreigner_extensions'
25
-
26
- if version >= '4.0'
27
- require_relative 'compat/4.0'
28
- elsif version >= '3.1'
29
- require_relative 'compat/3.1'
30
- else
31
- require_relative 'compat/3.0'
32
- end
33
- end
34
-
35
- # add some useful things for querying/comparing/dumping foreign keys
36
- module Immigrant
37
- module ForeignKeyExtensions
38
- def initialize(from_table, to_table, options, *args)
39
- options ||= {}
40
- options[:name] ||= "#{from_table}_#{options[:column]}_fk"
41
- super(from_table, to_table, options, *args)
42
- end
43
-
44
- def hash_key
45
- [from_table, options[:column]]
46
- end
47
-
48
- def to_ruby(action = :add)
49
- if action == :add
50
- dump_foreign_key(self)
51
- else
52
- "remove_foreign_key #{from_table.inspect}, " \
53
- ":name => #{options[:name].inspect}"
54
- end
55
- end
56
- end
12
+ require_relative 'compat/3.0'
57
13
  end
@@ -0,0 +1,23 @@
1
+ # add some useful things for querying/comparing/dumping foreign keys
2
+ module Immigrant
3
+ module ForeignKeyExtensions
4
+ def initialize(from_table, to_table, options, *args)
5
+ options ||= {}
6
+ options[:name] ||= "#{from_table}_#{options[:column]}_fk"
7
+ super(from_table, to_table, options, *args)
8
+ end
9
+
10
+ def hash_key
11
+ [from_table, options[:column]]
12
+ end
13
+
14
+ def to_ruby(action = :add)
15
+ if action == :add
16
+ dump_foreign_key(self)
17
+ else
18
+ "remove_foreign_key #{from_table.inspect}, " \
19
+ ":name => #{options[:name].inspect}"
20
+ end
21
+ end
22
+ end
23
+ end
data/lib/immigrant.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'active_support/all'
2
2
 
3
3
  module Immigrant
4
- class << self
4
+ class KeyFinder
5
5
  def infer_keys(db_keys = current_foreign_keys, classes = model_classes)
6
6
  database_keys = db_keys.inject({}) { |hash, foreign_key|
7
7
  hash[foreign_key.hash_key] = foreign_key
@@ -27,8 +27,12 @@ module Immigrant
27
27
 
28
28
  private
29
29
 
30
+ def tables
31
+ @tables ||= ActiveRecord::Base.connection.tables
32
+ end
33
+
30
34
  def current_foreign_keys
31
- ActiveRecord::Base.connection.tables.map{ |table|
35
+ tables.map{ |table|
32
36
  ActiveRecord::Base.connection.foreign_keys(table)
33
37
  }.flatten
34
38
  end
@@ -81,16 +85,17 @@ module Immigrant
81
85
  end
82
86
 
83
87
  def foreign_keys_for(klass)
88
+ return [] if klass.abstract_class? || !tables.include?(klass.table_name)
84
89
  candidate_reflections_for(klass).inject([]) do |result, reflection|
85
90
  begin
86
- result.concat foreign_key_for(klass, reflection)
91
+ result.concat foreign_keys_for_reflection(klass, reflection)
87
92
  rescue NameError # e.g. belongs_to :oops_this_is_not_a_table
88
93
  result
89
94
  end
90
95
  end
91
96
  end
92
97
 
93
- def foreign_key_for(klass, reflection)
98
+ def foreign_keys_for_reflection(klass, reflection)
94
99
  case reflection.macro
95
100
  when :belongs_to
96
101
  infer_belongs_to_keys(klass, reflection)
@@ -98,59 +103,112 @@ module Immigrant
98
103
  infer_has_n_keys(klass, reflection)
99
104
  when :has_and_belongs_to_many
100
105
  infer_habtm_keys(klass, reflection)
101
- else
102
- []
103
- end
106
+ end || []
104
107
  end
105
108
 
106
109
  def infer_belongs_to_keys(klass, reflection)
107
- return [] if reflection.name == :left_side # redundant and unusable reflection automagically created by HABTM
110
+ return if reflection.name == :left_side # redundant and unusable reflection automagically created by HABTM
111
+
112
+ from_table = klass.table_name
113
+ to_table = reflection.klass.table_name
114
+ column = reflection.send(FOREIGN_KEY).to_s
115
+ primary_key = (reflection.options[:primary_key] || reflection.klass.primary_key).to_s
116
+
117
+ return unless column_exists?(from_table, column)
118
+
108
119
  [
109
120
  ForeignKeyDefinition.new(
110
- klass.table_name,
111
- reflection.klass.table_name,
112
- :column => reflection.send(FOREIGN_KEY).to_s,
113
- :primary_key => (reflection.options[:primary_key] || reflection.klass.primary_key).to_s,
121
+ from_table,
122
+ to_table,
123
+ :column => column,
124
+ :primary_key => primary_key
114
125
  # although belongs_to can specify :dependent, it doesn't make
115
- # sense from a foreign key perspective
116
- ON_DELETE => nil
126
+ # sense from a foreign key perspective, so no :on_delete
117
127
  )
118
128
  ]
119
129
  end
120
130
 
121
131
  def infer_has_n_keys(klass, reflection)
132
+ from_table = reflection.klass.table_name
133
+ to_table = klass.table_name
134
+ column = reflection.send(FOREIGN_KEY).to_s
135
+ primary_key = (reflection.options[:primary_key] || klass.primary_key).to_s
136
+
137
+ actions = {}
138
+ if [:delete, :delete_all].include?(reflection.options[:dependent]) && !qualified_reflection?(reflection, klass)
139
+ actions = {:on_delete => :cascade, :on_update => :cascade}
140
+ end
141
+
142
+ return unless column_exists?(from_table, column)
143
+
122
144
  [
123
145
  ForeignKeyDefinition.new(
124
- reflection.klass.table_name,
125
- klass.table_name,
126
- :column => reflection.send(FOREIGN_KEY).to_s,
127
- :primary_key => (reflection.options[:primary_key] || klass.primary_key).to_s,
128
- ON_DELETE => [:delete, :delete_all].include?(reflection.options[:dependent]) && !qualified_reflection?(reflection, klass) ? :delete : nil
146
+ from_table,
147
+ to_table,
148
+ {
149
+ :column => column,
150
+ :primary_key => primary_key
151
+ }.merge(actions)
129
152
  )
130
153
  ]
131
154
  end
132
155
 
133
156
  def infer_habtm_keys(klass, reflection)
157
+ keys = []
158
+
134
159
  join_table = (reflection.respond_to?(:join_table) ? reflection.join_table : reflection.options[:join_table]).to_s
135
- [
136
- ForeignKeyDefinition.new(
160
+
161
+ left_to_table = klass.table_name
162
+ left_column = reflection.send(FOREIGN_KEY).to_s
163
+ left_primary_key = klass.primary_key.to_s
164
+ if column_exists?(join_table, left_column)
165
+ keys << ForeignKeyDefinition.new(
137
166
  join_table,
138
- klass.table_name,
139
- :column => reflection.send(FOREIGN_KEY).to_s,
140
- :primary_key => klass.primary_key.to_s,
141
- ON_DELETE => nil
142
- ),
143
- ForeignKeyDefinition.new(
167
+ left_to_table,
168
+ :column => left_column,
169
+ :primary_key => left_primary_key
170
+ )
171
+ end
172
+
173
+ right_to_table = reflection.klass.table_name
174
+ right_column = reflection.association_foreign_key.to_s
175
+ right_primary_key = reflection.klass.primary_key.to_s
176
+ if column_exists?(join_table, left_column)
177
+ keys << ForeignKeyDefinition.new(
144
178
  join_table,
145
- reflection.klass.table_name,
146
- :column => reflection.association_foreign_key.to_s,
147
- :primary_key => reflection.klass.primary_key.to_s,
148
- ON_DELETE => nil
179
+ right_to_table,
180
+ :column => right_column,
181
+ :primary_key => right_primary_key
149
182
  )
150
- ]
183
+ end
184
+
185
+ keys
186
+ end
187
+
188
+ def column_exists?(table_name, column_name)
189
+ columns_for(table_name).any? { |column| column.name == column_name }
190
+ end
191
+
192
+ def columns_for(table_name)
193
+ @columns ||= {}
194
+ @columns[table_name] ||= ActiveRecord::Base.connection.columns(table_name)
195
+ end
196
+
197
+ def qualified_reflection?(reflection, klass)
198
+ scope = reflection.scope
199
+ if scope.nil?
200
+ false
201
+ else
202
+ klass.instance_exec(*([nil]*scope.arity), &scope).where_clause.any?
203
+ end
204
+ rescue
205
+ # if there's an error evaluating the scope block or whatever, just
206
+ # err on the side of caution and assume there are conditions
207
+ true
151
208
  end
152
209
  end
153
210
  end
154
211
 
155
212
  require 'immigrant/loader'
213
+ require 'immigrant/foreign_key_extensions'
156
214
  require 'immigrant/railtie' if defined?(Rails)
data/test/helper.rb CHANGED
@@ -3,6 +3,7 @@ require 'bundler/setup'
3
3
  Bundler.require(:default)
4
4
 
5
5
  require 'minitest/autorun'
6
+ require 'minitest/stub_any_instance'
6
7
 
7
8
  require 'active_record'
8
9
  require_relative "compat"
@@ -3,22 +3,19 @@ require 'helper'
3
3
  class ImmigrantTest < ActiveSupport::TestCase
4
4
  include TestMethods
5
5
 
6
- class MockModel < ActiveRecord::Base
7
- self.abstract_class = true
8
- class << self
9
- def primary_key
10
- connection.primary_key(table_name)
11
- end
12
- def connection
13
- @connection ||= MockConnection.new
14
- end
6
+ ActiveRecord::Base.instance_eval do
7
+ def primary_key
8
+ connection.primary_key(table_name)
9
+ end
10
+ def connection
11
+ @connection ||= MockConnection.new
15
12
  end
16
13
 
17
14
  if ActiveRecord::VERSION::STRING >= '4.'
18
15
  # support old 3.x syntax for the sake of concise tests
19
- [:belongs_to, :has_one, :has_many, :has_and_belongs_to_many].each do |method|
20
- instance_eval <<-CODE, __FILE__, __LINE__ + 1
21
- def self.#{method}(assoc, scope = nil, options = {})
16
+ extend(Module.new{
17
+ [:belongs_to, :has_one, :has_many, :has_and_belongs_to_many].each do |method|
18
+ define_method method do |assoc, scope = nil, options = {}|
22
19
  if scope.is_a?(Hash)
23
20
  options = scope
24
21
  scope_opts = options.extract!(:conditions, :order)
@@ -28,8 +25,8 @@ class ImmigrantTest < ActiveSupport::TestCase
28
25
  end
29
26
  super assoc, scope, options
30
27
  end
31
- CODE
32
- end
28
+ end
29
+ })
33
30
  end
34
31
  end
35
32
 
@@ -40,18 +37,27 @@ class ImmigrantTest < ActiveSupport::TestCase
40
37
  def primary_key(table)
41
38
  table.to_s !~ /s_.*s\z/ ? 'id' : nil
42
39
  end
40
+ def tables
41
+ ActiveSupport::DescendantsTracker.direct_descendants(ActiveRecord::Base).map(&:table_name)
42
+ end
43
+ def columns(table_name)
44
+ AnyColumn.new
45
+ end
46
+ end
47
+
48
+ class AnyColumn
49
+ def any?
50
+ true
51
+ end
43
52
  end
44
53
 
45
54
  def teardown
46
- subclasses = ActiveSupport::DescendantsTracker.direct_descendants(MockModel)
55
+ subclasses = ActiveSupport::DescendantsTracker.direct_descendants(ActiveRecord::Base)
47
56
  subclasses.each do |subclass|
48
- Object.send(:remove_const, subclass.to_s)
57
+ subclass = subclass.to_s
58
+ Object.send(:remove_const, subclass) if subclass =~ /\A[A-Z]/ && Object.const_defined?(subclass)
49
59
  end
50
60
  subclasses.replace([])
51
- # also need to clear out other things under AR::Base, because as of
52
- # 4.1 there are automagical anonymous classes due to HABTM
53
- subclasses = ActiveSupport::DescendantsTracker.direct_descendants(ActiveRecord::Base)
54
- subclasses.replace([MockModel])
55
61
  end
56
62
 
57
63
  def given(code)
@@ -60,194 +66,183 @@ class ImmigrantTest < ActiveSupport::TestCase
60
66
  Object.class_eval code
61
67
  end
62
68
 
69
+ def infer_keys(db_keys = [])
70
+ keys = Immigrant::KeyFinder.new.infer_keys(db_keys).first
71
+ # ensure each key generates correctly
72
+ keys.each { |key| key.to_ruby(:add) }
73
+ keys.sort_by { |key| [key.from_table, key.to_table] }
74
+ end
75
+
63
76
  # basic scenarios
64
77
 
65
78
  test 'belongs_to should generate a foreign key' do
66
79
  given <<-CODE
67
- class Author < MockModel; end
68
- class Book < MockModel
80
+ class Author < ActiveRecord::Base; end
81
+ class Book < ActiveRecord::Base
69
82
  belongs_to :guy, :class_name => 'Author', :foreign_key => 'author_id'
70
83
  end
71
84
  CODE
72
85
 
73
- keys = Immigrant.infer_keys([]).first
74
- assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
75
86
  assert_equal(
76
87
  [foreign_key_definition(
77
88
  'books', 'authors',
78
- :column => 'author_id', :primary_key => 'id', Immigrant::ON_DELETE => nil
89
+ :column => 'author_id', :primary_key => 'id'
79
90
  )],
80
- keys
91
+ infer_keys
81
92
  )
82
93
  end
83
94
 
84
95
  test 'has_one should generate a foreign key' do
85
96
  given <<-CODE
86
- class Author < MockModel
97
+ class Author < ActiveRecord::Base
87
98
  has_one :piece_de_resistance, :class_name => 'Book', :order => "id DESC"
88
99
  end
89
- class Book < MockModel; end
100
+ class Book < ActiveRecord::Base; end
90
101
  CODE
91
102
 
92
- keys = Immigrant.infer_keys([]).first
93
- assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
94
103
  assert_equal(
95
104
  [foreign_key_definition(
96
105
  'books', 'authors',
97
- :column => 'author_id', :primary_key => 'id', Immigrant::ON_DELETE => nil
106
+ :column => 'author_id', :primary_key => 'id'
98
107
  )],
99
- keys
108
+ infer_keys
100
109
  )
101
110
  end
102
111
 
103
- test 'has_one :dependent => :delete should generate a foreign key with :on_delete => :delete' do
112
+ test 'has_one :dependent => :delete should generate a foreign key with :on_delete => :cascade' do
104
113
  given <<-CODE
105
- class Author < MockModel
114
+ class Author < ActiveRecord::Base
106
115
  has_one :book, :order => "id DESC", :dependent => :delete
107
116
  end
108
- class Book < MockModel; end
117
+ class Book < ActiveRecord::Base; end
109
118
  CODE
110
119
 
111
- keys = Immigrant.infer_keys([]).first
112
- assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
113
120
  assert_equal(
114
121
  [foreign_key_definition(
115
122
  'books', 'authors',
116
- :column => 'author_id', :primary_key => 'id', Immigrant::ON_DELETE => :delete
123
+ :column => 'author_id', :primary_key => 'id', :on_delete => :cascade, :on_update => :cascade
117
124
  )],
118
- keys
125
+ infer_keys
119
126
  )
120
127
  end
121
128
 
122
129
  test 'has_many should generate a foreign key' do
123
130
  given <<-CODE
124
- class Author < MockModel
131
+ class Author < ActiveRecord::Base
125
132
  has_many :babies, :class_name => 'Book'
126
133
  end
127
- class Book < MockModel; end
134
+ class Book < ActiveRecord::Base; end
128
135
  CODE
129
136
 
130
- keys = Immigrant.infer_keys([]).first
131
- assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
132
137
  assert_equal(
133
138
  [foreign_key_definition(
134
139
  'books', 'authors',
135
- :column => 'author_id', :primary_key => 'id', Immigrant::ON_DELETE => nil
140
+ :column => 'author_id', :primary_key => 'id'
136
141
  )],
137
- keys
142
+ infer_keys
138
143
  )
139
144
  end
140
145
 
141
- test 'has_many :dependent => :delete_all should generate a foreign key with :on_delete => :delete' do
146
+ test 'has_many :dependent => :delete_all should generate a foreign key with :on_delete => :cascade' do
142
147
  given <<-CODE
143
- class Author < MockModel
148
+ class Author < ActiveRecord::Base
144
149
  has_many :books, :dependent => :delete_all
145
150
  end
146
- class Book < MockModel; end
151
+ class Book < ActiveRecord::Base; end
147
152
  CODE
148
153
 
149
- keys = Immigrant.infer_keys([]).first
150
- assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
151
154
  assert_equal(
152
155
  [foreign_key_definition(
153
156
  'books', 'authors',
154
- :column => 'author_id', :primary_key => 'id', Immigrant::ON_DELETE => :delete
157
+ :column => 'author_id', :primary_key => 'id', :on_delete => :cascade, :on_update => :cascade
155
158
  )],
156
- keys
159
+ infer_keys
157
160
  )
158
161
  end
159
162
 
160
163
  test 'has_and_belongs_to_many should generate two foreign keys' do
161
164
  given <<-CODE
162
- class Author < MockModel
165
+ class Author < ActiveRecord::Base
163
166
  has_and_belongs_to_many :fans
164
167
  end
165
- class Fan < MockModel; end
168
+ class Fan < ActiveRecord::Base; end
166
169
  CODE
167
170
 
168
- keys = Immigrant.infer_keys([]).first
169
- assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
170
171
  assert_equal(
171
172
  [foreign_key_definition(
172
173
  'authors_fans', 'authors',
173
- :column => 'author_id', :primary_key => 'id', Immigrant::ON_DELETE => nil
174
+ :column => 'author_id', :primary_key => 'id'
174
175
  ),
175
176
  foreign_key_definition(
176
177
  'authors_fans', 'fans',
177
- :column => 'fan_id', :primary_key => 'id', Immigrant::ON_DELETE => nil
178
+ :column => 'fan_id', :primary_key => 'id'
178
179
  )],
179
- keys
180
+ infer_keys
180
181
  )
181
182
  end
182
183
 
183
184
  test 'has_and_belongs_to_many should respect the join_table' do
184
185
  given <<-CODE
185
- class Author < MockModel
186
+ class Author < ActiveRecord::Base
186
187
  has_and_belongs_to_many :fans, :join_table => :lols_wuts
187
188
  end
188
- class Fan < MockModel; end
189
+ class Fan < ActiveRecord::Base; end
189
190
  CODE
190
191
 
191
- keys = Immigrant.infer_keys([]).first
192
- assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
193
192
  assert_equal(
194
193
  [foreign_key_definition(
195
194
  'lols_wuts', 'authors',
196
- :column => 'author_id', :primary_key => 'id', Immigrant::ON_DELETE => nil
195
+ :column => 'author_id', :primary_key => 'id'
197
196
  ),
198
197
  foreign_key_definition(
199
198
  'lols_wuts', 'fans',
200
- :column => 'fan_id', :primary_key => 'id', Immigrant::ON_DELETE => nil
199
+ :column => 'fan_id', :primary_key => 'id'
201
200
  )],
202
- keys
201
+ infer_keys
203
202
  )
204
203
  end
205
204
 
206
205
  test 'conditional has_one/has_many associations should ignore :dependent' do
207
206
  given <<-CODE
208
- class Author < MockModel
207
+ class Author < ActiveRecord::Base
209
208
  has_many :articles, :conditions => "published", :dependent => :delete_all
210
209
  has_one :favorite_book, :class_name => 'Book',
211
210
  :conditions => "most_awesome", :dependent => :delete
212
211
  end
213
- class Book < MockModel; end
214
- class Article < MockModel; end
212
+ class Book < ActiveRecord::Base; end
213
+ class Article < ActiveRecord::Base; end
215
214
  CODE
216
215
 
217
- keys = Immigrant.infer_keys([]).first
218
- assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
219
216
  assert_equal(
220
217
  [foreign_key_definition(
221
218
  'articles', 'authors',
222
- :column => 'author_id', :primary_key => 'id', Immigrant::ON_DELETE => nil
219
+ :column => 'author_id', :primary_key => 'id'
223
220
  ),
224
221
  foreign_key_definition(
225
222
  'books', 'authors',
226
- :column => 'author_id', :primary_key => 'id', Immigrant::ON_DELETE => nil
223
+ :column => 'author_id', :primary_key => 'id'
227
224
  )],
228
- keys
225
+ infer_keys
229
226
  )
230
227
  end
231
228
 
232
229
  test 'primary_key should be respected' do
233
230
  given <<-CODE
234
- class User < MockModel
231
+ class User < ActiveRecord::Base
235
232
  has_many :emails, :primary_key => :email, :foreign_key => :to,
236
233
  :dependent => :destroy
237
234
  end
238
- class Email < MockModel
235
+ class Email < ActiveRecord::Base
239
236
  belongs_to :user, :primary_key => :email, :foreign_key => :to
240
237
  end
241
238
  CODE
242
239
 
243
- keys = Immigrant.infer_keys([]).first
244
- assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
245
240
  assert_equal(
246
241
  [foreign_key_definition(
247
242
  'emails', 'users',
248
- :column => 'to', :primary_key => 'email', Immigrant::ON_DELETE => nil
243
+ :column => 'to', :primary_key => 'email'
249
244
  )],
250
- keys
245
+ infer_keys
251
246
  )
252
247
  end
253
248
 
@@ -255,64 +250,58 @@ class ImmigrantTest < ActiveSupport::TestCase
255
250
 
256
251
  test 'STI should not generate duplicate foreign keys' do
257
252
  given <<-CODE
258
- class Company < MockModel; end
259
- class Employee < MockModel
253
+ class Company < ActiveRecord::Base; end
254
+ class Employee < ActiveRecord::Base
260
255
  belongs_to :company
261
256
  end
262
257
  class Manager < Employee; end
263
258
  CODE
264
259
 
265
260
  assert(Manager.reflections.present?)
266
- keys = Immigrant.infer_keys([]).first
267
- assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
268
261
  assert_equal(
269
262
  [foreign_key_definition(
270
263
  'employees', 'companies',
271
- :column => 'company_id', :primary_key => 'id', Immigrant::ON_DELETE => nil
264
+ :column => 'company_id', :primary_key => 'id'
272
265
  )],
273
- keys
266
+ infer_keys
274
267
  )
275
268
  end
276
269
 
277
270
  test 'complementary associations should not generate duplicate foreign keys' do
278
271
  given <<-CODE
279
- class Author < MockModel
272
+ class Author < ActiveRecord::Base
280
273
  has_many :books
281
274
  end
282
- class Book < MockModel
275
+ class Book < ActiveRecord::Base
283
276
  belongs_to :author
284
277
  end
285
278
  CODE
286
279
 
287
- keys = Immigrant.infer_keys([]).first
288
- assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
289
280
  assert_equal(
290
281
  [foreign_key_definition(
291
282
  'books', 'authors',
292
- :column => 'author_id', :primary_key => 'id', Immigrant::ON_DELETE => nil
283
+ :column => 'author_id', :primary_key => 'id'
293
284
  )],
294
- keys
285
+ infer_keys
295
286
  )
296
287
  end
297
288
 
298
289
  test 'redundant associations should not generate duplicate foreign keys' do
299
290
  given <<-CODE
300
- class Author < MockModel
291
+ class Author < ActiveRecord::Base
301
292
  has_many :books
302
293
  has_many :favorite_books, :class_name => 'Book', :conditions => "awesome"
303
294
  has_many :bad_books, :class_name => 'Book', :conditions => "amateur_hour"
304
295
  end
305
- class Book < MockModel; end
296
+ class Book < ActiveRecord::Base; end
306
297
  CODE
307
298
 
308
- keys = Immigrant.infer_keys([]).first
309
- assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
310
299
  assert_equal(
311
300
  [foreign_key_definition(
312
301
  'books', 'authors',
313
- :column => 'author_id', :primary_key => 'id', Immigrant::ON_DELETE => nil
302
+ :column => 'author_id', :primary_key => 'id'
314
303
  )],
315
- keys
304
+ infer_keys
316
305
  )
317
306
  end
318
307
 
@@ -323,33 +312,32 @@ class ImmigrantTest < ActiveSupport::TestCase
323
312
  database_keys = [
324
313
  foreign_key_definition(
325
314
  'articles', 'authors',
326
- :column => 'author_id', :primary_key => 'id', Immigrant::ON_DELETE => nil,
315
+ :column => 'author_id', :primary_key => 'id',
327
316
  :name => "doesn't_matter"
328
317
  ),
329
318
  foreign_key_definition(
330
319
  'books', 'authors', :column => 'author_id', :primary_key => 'id',
331
- Immigrant::ON_DELETE => :delete
320
+ :on_delete => :restrict, :on_update => :nullify
332
321
  )
333
322
  ]
334
323
 
335
324
  given <<-CODE
336
- class Author < MockModel
325
+ class Author < ActiveRecord::Base
337
326
  has_many :articles
338
327
  has_one :favorite_book, :class_name => 'Book',
339
328
  :conditions => "most_awesome"
340
329
  end
341
- class Book < MockModel; end
342
- class Article < MockModel; end
330
+ class Book < ActiveRecord::Base; end
331
+ class Article < ActiveRecord::Base; end
343
332
  CODE
344
333
 
345
- keys = Immigrant.infer_keys(database_keys).first
346
- assert_equal([], keys)
334
+ assert_equal([], infer_keys(database_keys))
347
335
  end
348
336
 
349
337
  if ActiveRecord::VERSION::STRING < '4.'
350
338
  test 'finder_sql associations should not generate foreign keys' do
351
339
  given <<-CODE
352
- class Author < MockModel
340
+ class Author < ActiveRecord::Base
353
341
  has_many :books, :finder_sql => <<-SQL
354
342
  SELECT *
355
343
  FROM books
@@ -357,79 +345,113 @@ class ImmigrantTest < ActiveSupport::TestCase
357
345
  ORDER BY RANDOM() LIMIT 5'
358
346
  SQL
359
347
  end
360
- class Book < MockModel; end
348
+ class Book < ActiveRecord::Base; end
361
349
  CODE
362
350
 
363
- keys = Immigrant.infer_keys([]).first
364
- assert_equal([], keys)
351
+ assert_equal([], infer_keys)
365
352
  end
366
353
  end
367
354
 
368
355
  test 'polymorphic associations should not generate foreign keys' do
369
356
  given <<-CODE
370
- class Property < MockModel
357
+ class Property < ActiveRecord::Base
371
358
  belongs_to :owner, :polymorphic => true
372
359
  end
373
- class Person < MockModel
360
+ class Person < ActiveRecord::Base
374
361
  has_many :properties, :as => :owner
375
362
  end
376
- class Corporation < MockModel
363
+ class Corporation < ActiveRecord::Base
377
364
  has_many :properties, :as => :owner
378
365
  end
379
366
  CODE
380
367
 
381
- keys = Immigrant.infer_keys([]).first
382
- assert_equal([], keys)
368
+ assert_equal([], infer_keys)
383
369
  end
384
370
 
385
371
  test 'has_many :through should not generate foreign keys' do
386
372
  given <<-CODE
387
- class Author < MockModel
373
+ class Author < ActiveRecord::Base
388
374
  has_many :authors_fans
389
375
  has_many :fans, :through => :authors_fans
390
376
  end
391
- class AuthorsFan < MockModel
377
+ class AuthorsFan < ActiveRecord::Base
392
378
  belongs_to :author
393
379
  belongs_to :fan
394
380
  end
395
- class Fan < MockModel
381
+ class Fan < ActiveRecord::Base
396
382
  has_many :authors_fans
397
383
  has_many :authors, :through => :authors_fans
398
384
  end
399
385
  CODE
400
386
 
401
- keys = Immigrant.infer_keys([]).first
402
- assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
403
387
  assert_equal(
404
388
  [foreign_key_definition(
405
389
  'authors_fans', 'authors',
406
- :column => 'author_id', :primary_key => 'id', Immigrant::ON_DELETE => nil
390
+ :column => 'author_id', :primary_key => 'id'
407
391
  ),
408
392
  foreign_key_definition(
409
393
  'authors_fans', 'fans',
410
- :column => 'fan_id', :primary_key => 'id', Immigrant::ON_DELETE => nil
394
+ :column => 'fan_id', :primary_key => 'id'
411
395
  )],
412
- keys
396
+ infer_keys
413
397
  )
414
398
  end
415
399
 
416
400
  test 'broken associations should not cause errors' do
417
401
  given <<-CODE
418
- class Author < MockModel; end
419
- class Book < MockModel
402
+ class Author < ActiveRecord::Base; end
403
+ class Book < ActiveRecord::Base
420
404
  belongs_to :author
421
405
  belongs_to :invalid
422
406
  end
423
407
  CODE
424
408
 
425
- keys = Immigrant.infer_keys([]).first
426
- assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
427
409
  assert_equal(
428
410
  [foreign_key_definition(
429
411
  'books', 'authors',
430
- :column => 'author_id', :primary_key => 'id', Immigrant::ON_DELETE => nil
412
+ :column => 'author_id', :primary_key => 'id'
431
413
  )],
432
- keys
414
+ infer_keys
433
415
  )
434
416
  end
417
+
418
+ test 'abstract classes should not generate a foreign key' do
419
+ given <<-CODE
420
+ class User < ActiveRecord::Base; end
421
+ class Widget < ActiveRecord::Base
422
+ self.abstract_class = true
423
+
424
+ belongs_to :user
425
+ end
426
+ CODE
427
+
428
+ assert_equal([], infer_keys)
429
+ end
430
+
431
+ test 'missing tables should not generate a foreign key' do
432
+ given <<-CODE
433
+ class User < ActiveRecord::Base; end
434
+ class Widget < ActiveRecord::Base
435
+ self.table_name = :wiiiidgets
436
+ belongs_to :user
437
+ end
438
+ CODE
439
+
440
+ MockConnection.stub_any_instance(:tables, ["users"]) do
441
+ assert_equal([], infer_keys)
442
+ end
443
+ end
444
+
445
+ test 'invalid columns should not generate a foreign key' do
446
+ given <<-CODE
447
+ class User < ActiveRecord::Base; end
448
+ class Widget < ActiveRecord::Base
449
+ belongs_to :user
450
+ end
451
+ CODE
452
+
453
+ MockConnection.stub_any_instance(:columns, []) do
454
+ assert_equal([], infer_keys)
455
+ end
456
+ end
435
457
  end
metadata CHANGED
@@ -1,30 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: immigrant
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
5
- prerelease:
4
+ version: 0.3.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Jon Jensen
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2014-12-11 00:00:00.000000000 Z
11
+ date: 2015-02-24 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: activerecord
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - ">="
20
18
  - !ruby/object:Gem::Version
21
19
  version: '3.0'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - ">="
28
25
  - !ruby/object:Gem::Version
29
26
  version: '3.0'
30
27
  description: Adds a generator for creating a foreign key migration based on your current
@@ -35,47 +32,48 @@ extensions: []
35
32
  extra_rdoc_files: []
36
33
  files:
37
34
  - LICENSE.txt
38
- - Rakefile
39
35
  - README.md
36
+ - Rakefile
40
37
  - lib/generators/USAGE
41
38
  - lib/generators/immigration_generator.rb
42
39
  - lib/generators/templates/immigration-pre-3.1.rb.erb
43
40
  - lib/generators/templates/immigration.rb.erb
41
+ - lib/immigrant.rb
42
+ - lib/immigrant/compat.rb
44
43
  - lib/immigrant/compat/3.0.rb
45
44
  - lib/immigrant/compat/3.1.rb
46
45
  - lib/immigrant/compat/4.0.rb
47
46
  - lib/immigrant/compat/4.2.rb
48
- - lib/immigrant/compat/foreigner_extensions.rb
49
- - lib/immigrant/compat.rb
47
+ - lib/immigrant/compat/5.0.rb
48
+ - lib/immigrant/compat/active_record.rb
49
+ - lib/immigrant/compat/foreigner.rb
50
+ - lib/immigrant/foreign_key_extensions.rb
50
51
  - lib/immigrant/loader.rb
51
52
  - lib/immigrant/railtie.rb
52
- - lib/immigrant.rb
53
53
  - test/compat.rb
54
54
  - test/helper.rb
55
55
  - test/immigrant_test.rb
56
56
  homepage: http://github.com/jenseng/immigrant
57
57
  licenses: []
58
+ metadata: {}
58
59
  post_install_message:
59
60
  rdoc_options: []
60
61
  require_paths:
61
62
  - lib
62
63
  required_ruby_version: !ruby/object:Gem::Requirement
63
- none: false
64
64
  requirements:
65
- - - ! '>='
65
+ - - ">="
66
66
  - !ruby/object:Gem::Version
67
67
  version: 1.8.7
68
68
  required_rubygems_version: !ruby/object:Gem::Requirement
69
- none: false
70
69
  requirements:
71
- - - ! '>='
70
+ - - ">="
72
71
  - !ruby/object:Gem::Version
73
72
  version: 1.3.5
74
73
  requirements: []
75
74
  rubyforge_project:
76
- rubygems_version: 1.8.23.2
75
+ rubygems_version: 2.2.2
77
76
  signing_key:
78
- specification_version: 3
77
+ specification_version: 4
79
78
  summary: Foreign key migration generator for Rails
80
79
  test_files: []
81
- has_rdoc:
@@ -1,5 +0,0 @@
1
- module Immigrant
2
- module ForeignKeyExtensions
3
- include Foreigner::SchemaDumper::ClassMethods
4
- end
5
- end