immigrant 0.1.6 → 0.1.7

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.
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # Immigrant
2
+ [<img src="https://secure.travis-ci.org/jenseng/immigrant.png?rvm=1.9.3" />](http://travis-ci.org/jenseng/immigrant)
3
+
4
+ Immigrant gives [Foreigner](https://github.com/matthuhiggins/foreigner) a
5
+ migration generator so you can effortlessly add missing foreign keys. This is
6
+ particularly helpful when you decide to add keys to an established Rails app.
7
+
8
+ Like Foreigner, Immigrant requires Rails 3.0 or greater.
9
+
10
+ ## Installation
11
+
12
+ Add the following to your Gemfile:
13
+
14
+ ```ruby
15
+ gem 'immigrant'
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```bash
21
+ rails generate immigration AddKeys
22
+ ```
23
+
24
+ This will create a migration named AddKeys which will have `add_foreign_key`
25
+ statements for any missing foreign keys. Immigrant infers missing ones by
26
+ evaluating the associations in your models (e.g. `belongs_to`, `has_many`, etc.).
27
+ Only missing keys will be added; existing ones will never be altered or
28
+ removed.
29
+
30
+ ## Considerations
31
+
32
+ If the data in your tables is bad, then the migration will fail to run
33
+ (obviously). IOW, ensure you don't have orphaned records **before** you try to
34
+ add foreign keys.
35
+
36
+ ## Known Issues
37
+
38
+ Immigrant currently only looks for foreign keys in `ActiveRecord::Base`'s
39
+ database. So if a model is using a different database connection and it has
40
+ foreign keys, Immigrant will incorrectly include them again in the generated
41
+ migration.
42
+
43
+ ## [Changelog](CHANGELOG.md)
44
+
45
+ ## License
46
+
47
+ Copyright (c) 2012-2014 Jon Jensen, released under the MIT license
@@ -10,10 +10,10 @@ class ImmigrationGenerator < ActiveRecord::Generators::Base
10
10
  $stderr.puts "NOTICE: #{key.options[:name]} has ON DELETE CASCADE. You should remove the :dependent option from the association to take advantage of this."
11
11
  end
12
12
  if @keys.present?
13
- template = ActiveRecord::VERSION::STRING < "3.1." ? "immigration-pre-3.1.rb" : "immigration.rb"
13
+ template = ActiveRecord::VERSION::STRING < "3.1." ? 'immigration-pre-3.1.rb.erb' : 'immigration.rb.erb'
14
14
  migration_template template, "db/migrate/#{file_name}.rb"
15
15
  else
16
- puts "Nothing to do"
16
+ puts 'Nothing to do'
17
17
  end
18
18
  end
19
19
 
data/lib/immigrant.rb CHANGED
@@ -39,11 +39,7 @@ module Immigrant
39
39
  end
40
40
 
41
41
  def model_classes
42
- classes = []
43
- ActiveRecord::Base.descendants.each do |model|
44
- classes << model.name.constantize
45
- end
46
- classes
42
+ ActiveRecord::Base.descendants
47
43
  end
48
44
 
49
45
  def model_keys(classes)
@@ -113,6 +109,7 @@ module Immigrant
113
109
  end
114
110
 
115
111
  def infer_belongs_to_keys(klass, reflection)
112
+ return [] if reflection.name == :left_side # redundant and unusable reflection automagically created by HABTM
116
113
  [
117
114
  Foreigner::ConnectionAdapters::ForeignKeyDefinition.new(
118
115
  klass.table_name,
data/test/helper.rb CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
2
2
  require 'bundler/setup'
3
3
  Bundler.require(:default)
4
4
 
5
- require 'test/unit'
5
+ require 'minitest/autorun'
6
6
  require 'active_record'
7
7
 
8
8
  require 'foreigner'
@@ -17,15 +17,16 @@ class ImmigrantTest < ActiveSupport::TestCase
17
17
  if ActiveRecord::VERSION::STRING >= '4.'
18
18
  # support old 3.x syntax for the sake of concise tests
19
19
  [:belongs_to, :has_one, :has_many, :has_and_belongs_to_many].each do |method|
20
- instance_eval <<-CODE
21
- def self.#{method}(assoc, options = {})
22
- args = [assoc]
23
- scope = options.extract!(:conditions, :order)
24
- if scope
25
- args.push lambda{ where(scope[:conditions]).order(scope[:order]) }
20
+ instance_eval <<-CODE, __FILE__, __LINE__ + 1
21
+ def self.#{method}(assoc, scope = nil, options = {})
22
+ if scope.is_a?(Hash)
23
+ options = scope
24
+ scope_opts = options.extract!(:conditions, :order)
25
+ scope = if scope_opts && scope_opts.present?
26
+ lambda{ |_| where(scope_opts[:conditions]).order(scope_opts[:order]) }
27
+ end
26
28
  end
27
- args.push options
28
- super *args
29
+ super assoc, scope, options
29
30
  end
30
31
  CODE
31
32
  end
@@ -44,18 +45,30 @@ class ImmigrantTest < ActiveSupport::TestCase
44
45
  def teardown
45
46
  subclasses = ActiveSupport::DescendantsTracker.direct_descendants(MockModel)
46
47
  subclasses.each do |subclass|
47
- ImmigrantTest.send(:remove_const, subclass.to_s.sub(/.*::/, ''))
48
+ Object.send(:remove_const, subclass.to_s)
48
49
  end
49
50
  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
+ end
56
+
57
+ def given(code)
58
+ # ugly little hack to get these temp classes not namespaced, so
59
+ # that generated HM/BT from HABTM will find the right class
60
+ Object.class_eval code
50
61
  end
51
62
 
52
63
  # basic scenarios
53
64
 
54
65
  test 'belongs_to should generate a foreign key' do
55
- class Author < MockModel; end
56
- class Book < MockModel
57
- belongs_to :guy, :class_name => 'Author', :foreign_key => 'author_id'
58
- end
66
+ given <<-CODE
67
+ class Author < MockModel; end
68
+ class Book < MockModel
69
+ belongs_to :guy, :class_name => 'Author', :foreign_key => 'author_id'
70
+ end
71
+ CODE
59
72
 
60
73
  keys = Immigrant.infer_keys([]).first
61
74
  assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
@@ -69,10 +82,12 @@ class ImmigrantTest < ActiveSupport::TestCase
69
82
  end
70
83
 
71
84
  test 'has_one should generate a foreign key' do
72
- class Author < MockModel
73
- has_one :piece_de_resistance, :class_name => 'Book', :order => "id DESC"
74
- end
75
- class Book < MockModel; end
85
+ given <<-CODE
86
+ class Author < MockModel
87
+ has_one :piece_de_resistance, :class_name => 'Book', :order => "id DESC"
88
+ end
89
+ class Book < MockModel; end
90
+ CODE
76
91
 
77
92
  keys = Immigrant.infer_keys([]).first
78
93
  assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
@@ -86,10 +101,12 @@ class ImmigrantTest < ActiveSupport::TestCase
86
101
  end
87
102
 
88
103
  test 'has_one :dependent => :delete should generate a foreign key with :dependent => :delete' do
89
- class Author < MockModel
90
- has_one :book, :order => "id DESC", :dependent => :delete
91
- end
92
- class Book < MockModel; end
104
+ given <<-CODE
105
+ class Author < MockModel
106
+ has_one :book, :order => "id DESC", :dependent => :delete
107
+ end
108
+ class Book < MockModel; end
109
+ CODE
93
110
 
94
111
  keys = Immigrant.infer_keys([]).first
95
112
  assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
@@ -103,10 +120,12 @@ class ImmigrantTest < ActiveSupport::TestCase
103
120
  end
104
121
 
105
122
  test 'has_many should generate a foreign key' do
106
- class Author < MockModel
107
- has_many :babies, :class_name => 'Book'
108
- end
109
- class Book < MockModel; end
123
+ given <<-CODE
124
+ class Author < MockModel
125
+ has_many :babies, :class_name => 'Book'
126
+ end
127
+ class Book < MockModel; end
128
+ CODE
110
129
 
111
130
  keys = Immigrant.infer_keys([]).first
112
131
  assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
@@ -120,10 +139,12 @@ class ImmigrantTest < ActiveSupport::TestCase
120
139
  end
121
140
 
122
141
  test 'has_many :dependent => :delete_all should generate a foreign key with :dependent => :delete' do
123
- class Author < MockModel
124
- has_many :books, :dependent => :delete_all
125
- end
126
- class Book < MockModel; end
142
+ given <<-CODE
143
+ class Author < MockModel
144
+ has_many :books, :dependent => :delete_all
145
+ end
146
+ class Book < MockModel; end
147
+ CODE
127
148
 
128
149
  keys = Immigrant.infer_keys([]).first
129
150
  assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
@@ -137,10 +158,12 @@ class ImmigrantTest < ActiveSupport::TestCase
137
158
  end
138
159
 
139
160
  test 'has_and_belongs_to_many should generate two foreign keys' do
140
- class Author < MockModel
141
- has_and_belongs_to_many :fans
142
- end
143
- class Fan < MockModel; end
161
+ given <<-CODE
162
+ class Author < MockModel
163
+ has_and_belongs_to_many :fans
164
+ end
165
+ class Fan < MockModel; end
166
+ CODE
144
167
 
145
168
  keys = Immigrant.infer_keys([]).first
146
169
  assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
@@ -158,10 +181,12 @@ class ImmigrantTest < ActiveSupport::TestCase
158
181
  end
159
182
 
160
183
  test 'has_and_belongs_to_many should respect the join_table' do
161
- class Author < MockModel
162
- has_and_belongs_to_many :fans, :join_table => :lols_wuts
163
- end
164
- class Fan < MockModel; end
184
+ given <<-CODE
185
+ class Author < MockModel
186
+ has_and_belongs_to_many :fans, :join_table => :lols_wuts
187
+ end
188
+ class Fan < MockModel; end
189
+ CODE
165
190
 
166
191
  keys = Immigrant.infer_keys([]).first
167
192
  assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
@@ -179,13 +204,15 @@ class ImmigrantTest < ActiveSupport::TestCase
179
204
  end
180
205
 
181
206
  test 'conditional has_one/has_many associations should ignore :dependent' do
182
- class Author < MockModel
183
- has_many :articles, :conditions => "published", :dependent => :delete_all
184
- has_one :favorite_book, :class_name => 'Book',
185
- :conditions => "most_awesome", :dependent => :delete
186
- end
187
- class Book < MockModel; end
188
- class Article < MockModel; end
207
+ given <<-CODE
208
+ class Author < MockModel
209
+ has_many :articles, :conditions => "published", :dependent => :delete_all
210
+ has_one :favorite_book, :class_name => 'Book',
211
+ :conditions => "most_awesome", :dependent => :delete
212
+ end
213
+ class Book < MockModel; end
214
+ class Article < MockModel; end
215
+ CODE
189
216
 
190
217
  keys = Immigrant.infer_keys([]).first
191
218
  assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
@@ -203,13 +230,15 @@ class ImmigrantTest < ActiveSupport::TestCase
203
230
  end
204
231
 
205
232
  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
233
+ given <<-CODE
234
+ class User < MockModel
235
+ has_many :emails, :primary_key => :email, :foreign_key => :to,
236
+ :dependent => :destroy
237
+ end
238
+ class Email < MockModel
239
+ belongs_to :user, :primary_key => :email, :foreign_key => :to
240
+ end
241
+ CODE
213
242
 
214
243
  keys = Immigrant.infer_keys([]).first
215
244
  assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
@@ -225,11 +254,13 @@ class ImmigrantTest < ActiveSupport::TestCase
225
254
  # (no) duplication
226
255
 
227
256
  test 'STI should not generate duplicate foreign keys' do
228
- class Company < MockModel; end
229
- class Employee < MockModel
230
- belongs_to :company
231
- end
232
- class Manager < Employee; end
257
+ given <<-CODE
258
+ class Company < MockModel; end
259
+ class Employee < MockModel
260
+ belongs_to :company
261
+ end
262
+ class Manager < Employee; end
263
+ CODE
233
264
 
234
265
  assert(Manager.reflections.present?)
235
266
  keys = Immigrant.infer_keys([]).first
@@ -244,12 +275,14 @@ class ImmigrantTest < ActiveSupport::TestCase
244
275
  end
245
276
 
246
277
  test 'complementary associations should not generate duplicate foreign keys' do
247
- class Author < MockModel
248
- has_many :books
249
- end
250
- class Book < MockModel
251
- belongs_to :author
252
- end
278
+ given <<-CODE
279
+ class Author < MockModel
280
+ has_many :books
281
+ end
282
+ class Book < MockModel
283
+ belongs_to :author
284
+ end
285
+ CODE
253
286
 
254
287
  keys = Immigrant.infer_keys([]).first
255
288
  assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
@@ -263,12 +296,14 @@ class ImmigrantTest < ActiveSupport::TestCase
263
296
  end
264
297
 
265
298
  test 'redundant associations should not generate duplicate foreign keys' do
266
- class Author < MockModel
267
- has_many :books
268
- has_many :favorite_books, :class_name => 'Book', :conditions => "awesome"
269
- has_many :bad_books, :class_name => 'Book', :conditions => "amateur_hour"
270
- end
271
- class Book < MockModel; end
299
+ given <<-CODE
300
+ class Author < MockModel
301
+ has_many :books
302
+ has_many :favorite_books, :class_name => 'Book', :conditions => "awesome"
303
+ has_many :bad_books, :class_name => 'Book', :conditions => "amateur_hour"
304
+ end
305
+ class Book < MockModel; end
306
+ CODE
272
307
 
273
308
  keys = Immigrant.infer_keys([]).first
274
309
  assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
@@ -297,13 +332,15 @@ class ImmigrantTest < ActiveSupport::TestCase
297
332
  )
298
333
  ]
299
334
 
300
- class Author < MockModel
301
- has_many :articles
302
- has_one :favorite_book, :class_name => 'Book',
303
- :conditions => "most_awesome"
304
- end
305
- class Book < MockModel; end
306
- class Article < MockModel; end
335
+ given <<-CODE
336
+ class Author < MockModel
337
+ has_many :articles
338
+ has_one :favorite_book, :class_name => 'Book',
339
+ :conditions => "most_awesome"
340
+ end
341
+ class Book < MockModel; end
342
+ class Article < MockModel; end
343
+ CODE
307
344
 
308
345
  keys = Immigrant.infer_keys(database_keys).first
309
346
  assert_equal([], keys)
@@ -311,15 +348,17 @@ class ImmigrantTest < ActiveSupport::TestCase
311
348
 
312
349
  if ActiveRecord::VERSION::STRING < '4.'
313
350
  test 'finder_sql associations should not generate foreign keys' do
314
- class Author < MockModel
315
- has_many :books, :finder_sql => <<-SQL
316
- SELECT *
317
- FROM books
318
- WHERE author_id = \#{id}
319
- ORDER BY RANDOM() LIMIT 5'
320
- SQL
321
- end
322
- class Book < MockModel; end
351
+ given <<-CODE
352
+ class Author < MockModel
353
+ has_many :books, :finder_sql => <<-SQL
354
+ SELECT *
355
+ FROM books
356
+ WHERE author_id = \\\#{id}
357
+ ORDER BY RANDOM() LIMIT 5'
358
+ SQL
359
+ end
360
+ class Book < MockModel; end
361
+ CODE
323
362
 
324
363
  keys = Immigrant.infer_keys([]).first
325
364
  assert_equal([], keys)
@@ -327,33 +366,37 @@ class ImmigrantTest < ActiveSupport::TestCase
327
366
  end
328
367
 
329
368
  test 'polymorphic associations should not generate foreign keys' do
330
- class Property < MockModel
331
- belongs_to :owner, :polymorphic => true
332
- end
333
- class Person < MockModel
334
- has_many :properties, :as => :owner
335
- end
336
- class Corporation < MockModel
337
- has_many :properties, :as => :owner
338
- end
369
+ given <<-CODE
370
+ class Property < MockModel
371
+ belongs_to :owner, :polymorphic => true
372
+ end
373
+ class Person < MockModel
374
+ has_many :properties, :as => :owner
375
+ end
376
+ class Corporation < MockModel
377
+ has_many :properties, :as => :owner
378
+ end
379
+ CODE
339
380
 
340
381
  keys = Immigrant.infer_keys([]).first
341
382
  assert_equal([], keys)
342
383
  end
343
384
 
344
385
  test 'has_many :through should not generate foreign keys' do
345
- class Author < MockModel
346
- has_many :authors_fans
347
- has_many :fans, :through => :authors_fans
348
- end
349
- class AuthorsFan < MockModel
350
- belongs_to :author
351
- belongs_to :fan
352
- end
353
- class Fan < MockModel
354
- has_many :authors_fans
355
- has_many :authors, :through => :authors_fans
356
- end
386
+ given <<-CODE
387
+ class Author < MockModel
388
+ has_many :authors_fans
389
+ has_many :fans, :through => :authors_fans
390
+ end
391
+ class AuthorsFan < MockModel
392
+ belongs_to :author
393
+ belongs_to :fan
394
+ end
395
+ class Fan < MockModel
396
+ has_many :authors_fans
397
+ has_many :authors, :through => :authors_fans
398
+ end
399
+ CODE
357
400
 
358
401
  keys = Immigrant.infer_keys([]).first
359
402
  assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
@@ -371,11 +414,13 @@ class ImmigrantTest < ActiveSupport::TestCase
371
414
  end
372
415
 
373
416
  test 'broken associations should not cause errors' do
374
- class Author < MockModel; end
375
- class Book < MockModel
376
- belongs_to :author
377
- belongs_to :invalid
378
- end
417
+ given <<-CODE
418
+ class Author < MockModel; end
419
+ class Book < MockModel
420
+ belongs_to :author
421
+ belongs_to :invalid
422
+ end
423
+ CODE
379
424
 
380
425
  keys = Immigrant.infer_keys([]).first
381
426
  assert_nothing_raised { keys.map { |key| key.to_ruby(:add) } }
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.6
4
+ version: 0.1.7
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: 2014-03-10 00:00:00.000000000 Z
12
+ date: 2014-04-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -48,16 +48,13 @@ description: Adds a generator for creating a foreign key migration based on your
48
48
  email: jenseng@gmail.com
49
49
  executables: []
50
50
  extensions: []
51
- extra_rdoc_files:
52
- - README.rdoc
51
+ extra_rdoc_files: []
53
52
  files:
54
53
  - LICENSE.txt
55
54
  - Rakefile
56
- - README.rdoc
55
+ - README.md
57
56
  - lib/generators/USAGE
58
57
  - lib/generators/immigration_generator.rb
59
- - lib/generators/templates/immigration-pre-3.1.rb
60
- - lib/generators/templates/immigration.rb
61
58
  - lib/immigrant/foreign_key_definition.rb
62
59
  - lib/immigrant/loader.rb
63
60
  - lib/immigrant/railtie.rb
data/README.rdoc DELETED
@@ -1,41 +0,0 @@
1
- = Immigrant
2
- {<img src="https://secure.travis-ci.org/jenseng/immigrant.png?rvm=1.9.3" />}[http://travis-ci.org/jenseng/immigrant]
3
-
4
- Immigrant gives {Foreigner}[https://github.com/matthuhiggins/foreigner] a
5
- migration generator so you can effortlessly add missing foreign keys. This is
6
- particularly helpful when you decide to add keys to an established Rails app.
7
-
8
- Like Foreigner, Immigrant requires Rails 3.0 or greater.
9
-
10
- == Installation
11
-
12
- Add the following to your Gemfile:
13
-
14
- gem 'immigrant'
15
-
16
- == Usage
17
-
18
- rails generate immigration AddKeys
19
-
20
- This will create a migration named AddKeys which will have add_foreign_key
21
- statements for any missing foreign keys. Immigrant infers missing ones by
22
- evaluating the associations in your models (e.g. belongs_to, has_many, etc.).
23
- Only missing keys will be added; existing ones will never be altered or
24
- removed.
25
-
26
- == Considerations
27
-
28
- If the data in your tables is bad, then the migration will fail to run
29
- (obviously). IOW, ensure you don't have orphaned records *before* you try to
30
- add foreign keys.
31
-
32
- == Known Issues
33
-
34
- Immigrant currently only looks for foreign keys in ActiveRecord::Base's
35
- database. So if a model is using a different database connection and it has
36
- foreign keys, Immigrant will incorrectly include them again in the generated
37
- migration.
38
-
39
- == License
40
-
41
- Copyright (c) 2013 Jon Jensen, released under the MIT license
@@ -1,13 +0,0 @@
1
- class <%= migration_class_name %> < ActiveRecord::Migration
2
- def self.up
3
- <% @keys.each do |key| -%>
4
- <%= key.to_ruby(:add) %>
5
- <%- end -%>
6
- end
7
-
8
- def self.down
9
- <% @keys.each do |key| -%>
10
- <%= key.to_ruby(:remove) %>
11
- <%- end -%>
12
- end
13
- end
@@ -1,7 +0,0 @@
1
- class <%= migration_class_name %> < ActiveRecord::Migration
2
- def change
3
- <% @keys.each do |key| -%>
4
- <%= key.to_ruby(:add) %>
5
- <%- end -%>
6
- end
7
- end