immigrant 0.1.5 → 0.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 (3) hide show
  1. data/lib/immigrant.rb +80 -44
  2. data/test/immigrant_test.rb +19 -0
  3. metadata +3 -3
data/lib/immigrant.rb CHANGED
@@ -50,16 +50,14 @@ module Immigrant
50
50
  # see what the models say there should be
51
51
  foreign_keys = {}
52
52
  warnings = {}
53
- classes.map{ |klass|
54
- foreign_keys_for(klass)
55
- }.flatten.uniq.each do |foreign_key|
53
+
54
+ candidate_model_keys(classes).each do |foreign_key|
56
55
  # we may have inferred it from several different places, e.g.
57
56
  # Bar.belongs_to :foo
58
57
  # Foo.has_many :bars
59
58
  # Foo.has_many :bazzes, :class_name => Bar
60
59
  # we need to make sure everything is legit and see if any of them
61
60
  # specify :dependent => :delete
62
- next if foreign_key.nil?
63
61
  if current_key = foreign_keys[foreign_key.hash_key]
64
62
  if current_key.to_table != foreign_key.to_table || current_key.options[:primary_key] != foreign_key.options[:primary_key]
65
63
  warnings[foreign_key.hash_key] ||= "Skipping #{foreign_key.from_table}.#{foreign_key.options[:column]}: it has multiple associations referencing different keys/tables."
@@ -74,56 +72,94 @@ module Immigrant
74
72
  [foreign_keys, warnings]
75
73
  end
76
74
 
77
- def foreign_keys_for(klass)
78
- fk_method = ActiveRecord::VERSION::STRING < '3.1.' ? :primary_key_name : :foreign_key
75
+ def candidate_model_keys(classes)
76
+ classes.inject([]) do |result, klass|
77
+ result.concat foreign_keys_for(klass)
78
+ end.uniq
79
+ end
79
80
 
80
- klass.reflections.values.reject{ |reflection|
81
+ def candidate_reflections_for(klass)
82
+ klass.reflections.values.reject do |reflection|
81
83
  # some associations can just be ignored, since:
82
84
  # 1. we aren't going to parse SQL
83
85
  # 2. foreign keys for :through associations will be handled by their
84
86
  # component has_one/has_many/belongs_to associations
85
87
  # 3. :polymorphic(/:as) associations can't have foreign keys
86
88
  (reflection.options.keys & [:finder_sql, :through, :polymorphic, :as]).present?
87
- }.map { |reflection|
89
+ end
90
+ end
91
+
92
+ def foreign_keys_for(klass)
93
+ candidate_reflections_for(klass).inject([]) do |result, reflection|
88
94
  begin
89
- case reflection.macro
90
- when :belongs_to
91
- Foreigner::ConnectionAdapters::ForeignKeyDefinition.new(
92
- klass.table_name, reflection.klass.table_name,
93
- :column => reflection.send(fk_method).to_s,
94
- :primary_key => reflection.klass.primary_key.to_s,
95
- # although belongs_to can specify :dependent, it doesn't make
96
- # sense from a foreign key perspective
97
- :dependent => nil
98
- )
99
- when :has_one, :has_many
100
- Foreigner::ConnectionAdapters::ForeignKeyDefinition.new(
101
- reflection.klass.table_name, klass.table_name,
102
- :column => reflection.send(fk_method).to_s,
103
- :primary_key => klass.primary_key.to_s,
104
- :dependent => [:delete, :delete_all].include?(reflection.options[:dependent]) && !qualified_reflection?(reflection, klass) ? :delete : nil
105
- )
106
- when :has_and_belongs_to_many
107
- join_table = (reflection.respond_to?(:join_table) ? reflection.join_table : reflection.options[:join_table]).to_s
108
- [
109
- Foreigner::ConnectionAdapters::ForeignKeyDefinition.new(
110
- join_table, klass.table_name,
111
- :column => reflection.send(fk_method).to_s,
112
- :primary_key => klass.primary_key.to_s,
113
- :dependent => nil
114
- ),
115
- Foreigner::ConnectionAdapters::ForeignKeyDefinition.new(
116
- join_table, reflection.klass.table_name,
117
- :column => reflection.association_foreign_key.to_s,
118
- :primary_key => reflection.klass.primary_key.to_s,
119
- :dependent => nil
120
- )
121
- ]
122
- end
95
+ result.concat foreign_key_for(klass, reflection)
123
96
  rescue NameError # e.g. belongs_to :oops_this_is_not_a_table
124
- []
97
+ result
125
98
  end
126
- }.flatten
99
+ end
100
+ end
101
+
102
+ def foreign_key_for(klass, reflection)
103
+ case reflection.macro
104
+ when :belongs_to
105
+ infer_belongs_to_keys(klass, reflection)
106
+ when :has_one, :has_many
107
+ infer_has_n_keys(klass, reflection)
108
+ when :has_and_belongs_to_many
109
+ infer_habtm_keys(klass, reflection)
110
+ else
111
+ []
112
+ end
113
+ end
114
+
115
+ def infer_belongs_to_keys(klass, reflection)
116
+ [
117
+ Foreigner::ConnectionAdapters::ForeignKeyDefinition.new(
118
+ klass.table_name,
119
+ reflection.klass.table_name,
120
+ :column => reflection.send(fk_method).to_s,
121
+ :primary_key => (reflection.options[:primary_key] || reflection.klass.primary_key).to_s,
122
+ # although belongs_to can specify :dependent, it doesn't make
123
+ # sense from a foreign key perspective
124
+ :dependent => nil
125
+ )
126
+ ]
127
+ end
128
+
129
+ def infer_has_n_keys(klass, reflection)
130
+ [
131
+ Foreigner::ConnectionAdapters::ForeignKeyDefinition.new(
132
+ reflection.klass.table_name,
133
+ klass.table_name,
134
+ :column => reflection.send(fk_method).to_s,
135
+ :primary_key => (reflection.options[:primary_key] || klass.primary_key).to_s,
136
+ :dependent => [:delete, :delete_all].include?(reflection.options[:dependent]) && !qualified_reflection?(reflection, klass) ? :delete : nil
137
+ )
138
+ ]
139
+ end
140
+
141
+ def infer_habtm_keys(klass, reflection)
142
+ join_table = (reflection.respond_to?(:join_table) ? reflection.join_table : reflection.options[:join_table]).to_s
143
+ [
144
+ Foreigner::ConnectionAdapters::ForeignKeyDefinition.new(
145
+ join_table,
146
+ klass.table_name,
147
+ :column => reflection.send(fk_method).to_s,
148
+ :primary_key => klass.primary_key.to_s,
149
+ :dependent => nil
150
+ ),
151
+ Foreigner::ConnectionAdapters::ForeignKeyDefinition.new(
152
+ join_table,
153
+ reflection.klass.table_name,
154
+ :column => reflection.association_foreign_key.to_s,
155
+ :primary_key => reflection.klass.primary_key.to_s,
156
+ :dependent => nil
157
+ )
158
+ ]
159
+ end
160
+
161
+ def fk_method
162
+ ActiveRecord::VERSION::STRING < '3.1.' ? :primary_key_name : :foreign_key
127
163
  end
128
164
 
129
165
  def qualified_reflection?(reflection, klass)
@@ -202,6 +202,25 @@ class ImmigrantTest < ActiveSupport::TestCase
202
202
  )
203
203
  end
204
204
 
205
+ test 'primary_key should be respected' do
206
+ class User < MockModel
207
+ has_many :emails, :primary_key => :email, :foreign_key => :to,
208
+ :dependent => :destroy
209
+ end
210
+ class Email < MockModel
211
+ belongs_to :user, :primary_key => :email, :foreign_key => :to
212
+ end
213
+
214
+ keys = Immigrant.infer_keys([]).first
215
+ assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
216
+ assert_equal(
217
+ [foreign_key_definition(
218
+ 'emails', 'users',
219
+ :column => 'to', :primary_key => 'email', :dependent => nil
220
+ )],
221
+ keys
222
+ )
223
+ end
205
224
 
206
225
  # (no) duplication
207
226
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: immigrant
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-08 00:00:00.000000000 Z
12
+ date: 2014-03-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -84,7 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
84
  version: 1.3.5
85
85
  requirements: []
86
86
  rubyforge_project:
87
- rubygems_version: 1.8.25
87
+ rubygems_version: 1.8.23
88
88
  signing_key:
89
89
  specification_version: 3
90
90
  summary: Migration generator for Foreigner