schema_plus 0.1.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/.gitignore +25 -0
  2. data/Gemfile +3 -0
  3. data/MIT-LICENSE +25 -0
  4. data/README.rdoc +147 -0
  5. data/Rakefile +70 -0
  6. data/init.rb +1 -0
  7. data/lib/schema_plus/active_record/associations.rb +211 -0
  8. data/lib/schema_plus/active_record/base.rb +81 -0
  9. data/lib/schema_plus/active_record/connection_adapters/abstract_adapter.rb +96 -0
  10. data/lib/schema_plus/active_record/connection_adapters/column.rb +55 -0
  11. data/lib/schema_plus/active_record/connection_adapters/foreign_key_definition.rb +115 -0
  12. data/lib/schema_plus/active_record/connection_adapters/index_definition.rb +51 -0
  13. data/lib/schema_plus/active_record/connection_adapters/mysql_adapter.rb +111 -0
  14. data/lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb +163 -0
  15. data/lib/schema_plus/active_record/connection_adapters/schema_statements.rb +39 -0
  16. data/lib/schema_plus/active_record/connection_adapters/sqlite3_adapter.rb +78 -0
  17. data/lib/schema_plus/active_record/connection_adapters/table_definition.rb +130 -0
  18. data/lib/schema_plus/active_record/migration.rb +220 -0
  19. data/lib/schema_plus/active_record/schema.rb +27 -0
  20. data/lib/schema_plus/active_record/schema_dumper.rb +122 -0
  21. data/lib/schema_plus/active_record/validations.rb +139 -0
  22. data/lib/schema_plus/railtie.rb +12 -0
  23. data/lib/schema_plus/version.rb +3 -0
  24. data/lib/schema_plus.rb +248 -0
  25. data/schema_plus.gemspec +37 -0
  26. data/schema_plus.gemspec.rails3.0 +36 -0
  27. data/schema_plus.gemspec.rails3.1 +36 -0
  28. data/spec/association_spec.rb +529 -0
  29. data/spec/connections/mysql/connection.rb +18 -0
  30. data/spec/connections/mysql2/connection.rb +18 -0
  31. data/spec/connections/postgresql/connection.rb +15 -0
  32. data/spec/connections/sqlite3/connection.rb +14 -0
  33. data/spec/foreign_key_definition_spec.rb +23 -0
  34. data/spec/foreign_key_spec.rb +142 -0
  35. data/spec/index_definition_spec.rb +139 -0
  36. data/spec/index_spec.rb +71 -0
  37. data/spec/migration_spec.rb +405 -0
  38. data/spec/models/comment.rb +2 -0
  39. data/spec/models/post.rb +2 -0
  40. data/spec/models/user.rb +2 -0
  41. data/spec/references_spec.rb +78 -0
  42. data/spec/schema/auto_schema.rb +23 -0
  43. data/spec/schema/core_schema.rb +21 -0
  44. data/spec/schema_dumper_spec.rb +167 -0
  45. data/spec/schema_spec.rb +71 -0
  46. data/spec/spec_helper.rb +59 -0
  47. data/spec/support/extensions/active_model.rb +13 -0
  48. data/spec/support/helpers.rb +16 -0
  49. data/spec/support/matchers/automatic_foreign_key_matchers.rb +2 -0
  50. data/spec/support/matchers/have_index.rb +52 -0
  51. data/spec/support/matchers/reference.rb +66 -0
  52. data/spec/support/reference.rb +66 -0
  53. data/spec/validations_spec.rb +294 -0
  54. data/spec/views_spec.rb +140 -0
  55. metadata +269 -0
@@ -0,0 +1,37 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "schema_plus/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "schema_plus"
7
+ s.version = SchemaPlus::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Ronen Barzel", "Michał Łomnicki"]
10
+ s.email = ["ronen@barzel.org", "michal.lomnicki@gmail.com"]
11
+ s.homepage = "https://github.com/lomba/schema_plus"
12
+ s.summary = "Enhances ActiveRecord schema mechanism, including more DRY index creation and support for foreign key constraints and views."
13
+ s.description = "SchemaPlus is an ActiveRecord extension that provides enhanced capabilities for schema definition and querying, including: enhanced and more DRY index capabilities, support and automation for foreign key constraints, and support for views."
14
+
15
+
16
+ s.rubyforge_project = "schema_plus"
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+
23
+ s.add_dependency("rails")
24
+ s.add_dependency("valuable")
25
+
26
+ s.add_development_dependency("rake", "~> 0.8.7")
27
+ s.add_development_dependency("rails", ">= 3.1.0.rc1")
28
+ s.add_development_dependency("rspec")
29
+ s.add_development_dependency("pg")
30
+ s.add_development_dependency("mysql")
31
+ s.add_development_dependency("mysql2")
32
+ s.add_development_dependency("sqlite3")
33
+ s.add_development_dependency("simplecov")
34
+ s.add_development_dependency("simplecov-gem-adapter")
35
+ s.add_development_dependency("ruby-debug19") if RUBY_VERSION >= "1.9.2"
36
+ end
37
+
@@ -0,0 +1,36 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "schema_plus/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "schema_plus"
7
+ s.version = SchemaPlus::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Ronen Barzel", "Michał Łomnicki"]
10
+ s.email = ["ronen@barzel.org", "michal.lomnicki@gmail.com"]
11
+ s.homepage = "https://github.com/lomba/schema_plus"
12
+ s.summary = "Provides ActiveRecord support for foreign keys, database defined validations and associations."
13
+ s.description = "SchemaPlus is an ActiveRecord extension that provides support for defining foreign keys and indexes in database migrations and schemas, as well as for defining model validations and associations based on the database."
14
+
15
+ s.rubyforge_project = "schema_plus"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency("rails")
23
+ s.add_dependency("valuable")
24
+
25
+ s.add_development_dependency("rake", "~> 0.8.7")
26
+ s.add_development_dependency("rails", "~> 3.0")
27
+ s.add_development_dependency("rspec")
28
+ s.add_development_dependency("pg")
29
+ s.add_development_dependency("mysql")
30
+ s.add_development_dependency("mysql2", "0.2.6")
31
+ s.add_development_dependency("sqlite3")
32
+ s.add_development_dependency("simplecov")
33
+ s.add_development_dependency("simplecov-gem-adapter")
34
+ s.add_development_dependency("ruby-debug19") if RUBY_VERSION >= "1.9.2"
35
+ end
36
+
@@ -0,0 +1,36 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "schema_plus/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "schema_plus"
7
+ s.version = SchemaPlus::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Ronen Barzel", "Michał Łomnicki"]
10
+ s.email = ["ronen@barzel.org", "michal.lomnicki@gmail.com"]
11
+ s.homepage = "https://github.com/lomba/schema_plus"
12
+ s.summary = "Provides ActiveRecord support for foreign keys, database defined validations and associations."
13
+ s.description = "SchemaPlus is an ActiveRecord extension that provides support for defining foreign keys and indexes in database migrations and schemas, as well as for defining model validations and associations based on the database."
14
+
15
+ s.rubyforge_project = "schema_plus"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency("rails")
23
+ s.add_dependency("valuable")
24
+
25
+ s.add_development_dependency("rake", "~> 0.8.7")
26
+ s.add_development_dependency("rails", ">= 3.1.0.rc1")
27
+ s.add_development_dependency("rspec")
28
+ s.add_development_dependency("pg")
29
+ s.add_development_dependency("mysql")
30
+ s.add_development_dependency("mysql2")
31
+ s.add_development_dependency("sqlite3")
32
+ s.add_development_dependency("simplecov")
33
+ s.add_development_dependency("simplecov-gem-adapter")
34
+ s.add_development_dependency("ruby-debug19") if RUBY_VERSION >= "1.9.2"
35
+ end
36
+
@@ -0,0 +1,529 @@
1
+ # encoding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
+
4
+ describe ActiveRecord::Base do
5
+ include SchemaPlusHelpers
6
+
7
+ after(:each) do
8
+ remove_all_models
9
+ end
10
+
11
+ around(:each) do |example|
12
+ with_fk_auto_create(&example)
13
+ end
14
+
15
+ context "in basic case" do
16
+ before(:each) do
17
+ create_tables(
18
+ "posts", {}, {},
19
+ "comments", {}, { :post_id => {} }
20
+ )
21
+ class Post < ActiveRecord::Base ; end
22
+ class Comment < ActiveRecord::Base ; end
23
+ end
24
+ it "should create belongs_to association" do
25
+ reflection = Comment.reflect_on_association(:post)
26
+ reflection.should_not be_nil
27
+ reflection.macro.should == :belongs_to
28
+ reflection.options[:class_name].should == "Post"
29
+ reflection.options[:foreign_key].should == "post_id"
30
+ end
31
+ it "should create has_many association" do
32
+ reflection = Post.reflect_on_association(:comments)
33
+ reflection.should_not be_nil
34
+ reflection.macro.should == :has_many
35
+ reflection.options[:class_name].should == "Comment"
36
+ reflection.options[:foreign_key].should == "post_id"
37
+ end
38
+ it "shouldn't raise an exception when model is instantiated" do
39
+ expect { Post.new }.should_not raise_error
40
+ end
41
+ end
42
+
43
+ it "should override auto_create negatively" do
44
+ with_associations_auto_create(true) do
45
+ create_tables(
46
+ "posts", {}, {},
47
+ "comments", {}, { :post_id => {} }
48
+ )
49
+ class Post < ActiveRecord::Base
50
+ schema_plus :associations => { :auto_create => false }
51
+ end
52
+ class Comment < ActiveRecord::Base ; end
53
+ Post.reflect_on_association(:comments).should be_nil
54
+ Comment.reflect_on_association(:post).should_not be_nil
55
+ end
56
+ end
57
+
58
+ context "with multiple associations of all types" do
59
+ before(:each) do
60
+ create_tables(
61
+ "owners", {}, {},
62
+ "colors", {}, {},
63
+ "widgets", {}, {
64
+ :owner_id => {},
65
+ },
66
+ "parts", {}, { :widget_id => {} },
67
+ "manifests", {}, { :widget_id => { :index => {:unique => true}} },
68
+ "colors_widgets", {:id => false}, { :widget_id => {}, :color_id => {} }
69
+ )
70
+ end
71
+
72
+ def check_reflections(hash)
73
+ hash.each do |key, val|
74
+ reflection = Widget.reflect_on_association(key)
75
+ case val
76
+ when true then reflection.should_not be_nil
77
+ else reflection.should be_nil
78
+ end
79
+ end
80
+ end
81
+
82
+ it "should default as expected" do
83
+ class Widget < ActiveRecord::Base ; end
84
+ check_reflections(:owner => true, :colors => true, :parts => true, :manifest => true)
85
+ end
86
+
87
+ it "should respect :only" do
88
+ class Widget < ActiveRecord::Base
89
+ schema_plus :associations => { :only => :owner }
90
+ end
91
+ check_reflections(:owner => true, :colors => false, :parts => false, :manifest => false)
92
+ end
93
+
94
+ it "should respect :except" do
95
+ class Widget < ActiveRecord::Base
96
+ schema_plus :associations => { :except => :owner }
97
+ end
98
+ check_reflections(:owner => false, :colors => true, :parts => true, :manifest => true)
99
+ end
100
+
101
+ it "should respect :only_type :belongs_to" do
102
+ class Widget < ActiveRecord::Base
103
+ schema_plus :associations => { :only_type => :belongs_to }
104
+ end
105
+ check_reflections(:owner => true, :colors => false, :parts => false, :manifest => false)
106
+ end
107
+
108
+ it "should respect :except_type :belongs_to" do
109
+ class Widget < ActiveRecord::Base
110
+ schema_plus :associations => { :except_type => :belongs_to }
111
+ end
112
+ check_reflections(:owner => false, :colors => true, :parts => true, :manifest => true)
113
+ end
114
+
115
+ it "should respect :only_type :has_many" do
116
+ class Widget < ActiveRecord::Base
117
+ schema_plus :associations => { :only_type => :has_many }
118
+ end
119
+ check_reflections(:owner => false, :colors => false, :parts => true, :manifest => false)
120
+ end
121
+
122
+ it "should respect :except_type :has_many" do
123
+ class Widget < ActiveRecord::Base
124
+ schema_plus :associations => { :except_type => :has_many }
125
+ end
126
+ check_reflections(:owner => true, :colors => true, :parts => false, :manifest => true)
127
+ end
128
+
129
+ it "should respect :only_type :has_one" do
130
+ class Widget < ActiveRecord::Base
131
+ schema_plus :associations => { :only_type => :has_one }
132
+ end
133
+ check_reflections(:owner => false, :colors => false, :parts => false, :manifest => true)
134
+ end
135
+
136
+ it "should respect :except_type :has_one" do
137
+ class Widget < ActiveRecord::Base
138
+ schema_plus :associations => { :except_type => :has_one }
139
+ end
140
+ check_reflections(:owner => true, :colors => true, :parts => true, :manifest => false)
141
+ end
142
+
143
+ it "should respect :only_type :has_and_belongs_to_many" do
144
+ class Widget < ActiveRecord::Base
145
+ schema_plus :associations => { :only_type => :has_and_belongs_to_many }
146
+ end
147
+ check_reflections(:owner => false, :colors => true, :parts => false, :manifest => false)
148
+ end
149
+
150
+ it "should respect :except_type :has_and_belongs_to_many" do
151
+ class Widget < ActiveRecord::Base
152
+ schema_plus :associations => { :except_type => :has_and_belongs_to_many }
153
+ end
154
+ check_reflections(:owner => true, :colors => false, :parts => true, :manifest => true)
155
+ end
156
+
157
+ end
158
+
159
+ it "should override auto_create positively" do
160
+ with_associations_auto_create(false) do
161
+ create_tables(
162
+ "posts", {}, {},
163
+ "comments", {}, { :post_id => {} }
164
+ )
165
+ class Post < ActiveRecord::Base
166
+ schema_plus :associations => { :auto_create => true }
167
+ end
168
+ class Comment < ActiveRecord::Base ; end
169
+ Post.reflect_on_association(:comments).should_not be_nil
170
+ Comment.reflect_on_association(:post).should be_nil
171
+ end
172
+ end
173
+
174
+
175
+ context "with unique index" do
176
+ before(:each) do
177
+ create_tables(
178
+ "posts", {}, {},
179
+ "comments", {}, { :post_id => {:index => { :unique => true} } }
180
+ )
181
+ class Post < ActiveRecord::Base ; end
182
+ class Comment < ActiveRecord::Base ; end
183
+ end
184
+ it "should create has_one association" do
185
+ reflection = Post.reflect_on_association(:comment)
186
+ reflection.should_not be_nil
187
+ reflection.macro.should == :has_one
188
+ reflection.options[:class_name].should == "Comment"
189
+ reflection.options[:foreign_key].should == "post_id"
190
+ end
191
+ end
192
+
193
+ context "with prefixed column names" do
194
+ before(:each) do
195
+ create_tables(
196
+ "posts", {}, {},
197
+ "comments", {}, { :subject_post_id => { :references => :posts} }
198
+ )
199
+ class Post < ActiveRecord::Base ; end
200
+ class Comment < ActiveRecord::Base ; end
201
+ end
202
+ it "should name belongs_to according to column" do
203
+ reflection = Comment.reflect_on_association(:subject_post)
204
+ reflection.should_not be_nil
205
+ reflection.macro.should == :belongs_to
206
+ reflection.options[:class_name].should == "Post"
207
+ reflection.options[:foreign_key].should == "subject_post_id"
208
+ end
209
+
210
+ it "should name has_many using 'as column'" do
211
+ reflection = Post.reflect_on_association(:comments_as_subject)
212
+ reflection.should_not be_nil
213
+ reflection.macro.should == :has_many
214
+ reflection.options[:class_name].should == "Comment"
215
+ reflection.options[:foreign_key].should == "subject_post_id"
216
+ end
217
+ end
218
+
219
+ context "with suffixed column names" do
220
+ before(:each) do
221
+ create_tables(
222
+ "posts", {}, {},
223
+ "comments", {}, { :post_cited => { :references => :posts} }
224
+ )
225
+ class Post < ActiveRecord::Base ; end
226
+ class Comment < ActiveRecord::Base ; end
227
+ end
228
+ it "should name belongs_to according to column" do
229
+ reflection = Comment.reflect_on_association(:post_cited)
230
+ reflection.should_not be_nil
231
+ reflection.macro.should == :belongs_to
232
+ reflection.options[:class_name].should == "Post"
233
+ reflection.options[:foreign_key].should == "post_cited"
234
+ end
235
+
236
+ it "should name has_many using 'as column'" do
237
+ reflection = Post.reflect_on_association(:comments_as_cited)
238
+ reflection.should_not be_nil
239
+ reflection.macro.should == :has_many
240
+ reflection.options[:class_name].should == "Comment"
241
+ reflection.options[:foreign_key].should == "post_cited"
242
+ end
243
+ end
244
+
245
+ context "with arbitrary column names" do
246
+ before(:each) do
247
+ create_tables(
248
+ "posts", {}, {},
249
+ "comments", {}, { :subject => {:references => :posts} }
250
+ )
251
+ class Post < ActiveRecord::Base ; end
252
+ class Comment < ActiveRecord::Base ; end
253
+ end
254
+ it "should name belongs_to according to column" do
255
+ reflection = Comment.reflect_on_association(:subject)
256
+ reflection.should_not be_nil
257
+ reflection.macro.should == :belongs_to
258
+ reflection.options[:class_name].should == "Post"
259
+ reflection.options[:foreign_key].should == "subject"
260
+ end
261
+
262
+ it "should name has_many using 'as column'" do
263
+ reflection = Post.reflect_on_association(:comments_as_subject)
264
+ reflection.should_not be_nil
265
+ reflection.macro.should == :has_many
266
+ reflection.options[:class_name].should == "Comment"
267
+ reflection.options[:foreign_key].should == "subject"
268
+ end
269
+ end
270
+
271
+
272
+ context "with position" do
273
+ before(:each) do
274
+ create_tables(
275
+ "posts", {}, {},
276
+ "comments", {}, { :post_id => {}, :position => {} }
277
+ )
278
+ class Post < ActiveRecord::Base ; end
279
+ class Comment < ActiveRecord::Base ; end
280
+ end
281
+ it "should create ordered has_many association" do
282
+ reflection = Post.reflect_on_association(:comments)
283
+ reflection.should_not be_nil
284
+ reflection.macro.should == :has_many
285
+ reflection.options[:class_name].should == "Comment"
286
+ reflection.options[:foreign_key].should == "post_id"
287
+ reflection.options[:order].to_s.should == "position"
288
+ end
289
+ end
290
+
291
+ context "regarding parent-child relationships" do
292
+
293
+ let (:migration) {ActiveRecord::Migration}
294
+
295
+ before(:each) do
296
+ create_tables(
297
+ "nodes", {:foreign_keys => {:auto_index => false}}, { :parent_id => {} }
298
+ )
299
+ end
300
+
301
+ it "should use children as the inverse of parent" do
302
+ class Node < ActiveRecord::Base ; end
303
+ reflection = Node.reflect_on_association(:children)
304
+ reflection.should_not be_nil
305
+ end
306
+
307
+ it "should use child as the singular inverse of parent" do
308
+ migration.suppress_messages do
309
+ migration.add_index(:nodes, :parent_id, :unique => true)
310
+ end
311
+ class Node < ActiveRecord::Base ; end
312
+ reflection = Node.reflect_on_association(:child)
313
+ reflection.should_not be_nil
314
+ end
315
+ end
316
+
317
+
318
+ context "regarding concise names" do
319
+
320
+ def prefix_one
321
+ create_tables(
322
+ "posts", {}, {},
323
+ "post_comments", {}, { :post_id => {} }
324
+ )
325
+ Object.const_set(:Post, Class.new(ActiveRecord::Base))
326
+ Object.const_set(:PostComment, Class.new(ActiveRecord::Base))
327
+ end
328
+
329
+ def suffix_one
330
+ create_tables(
331
+ "posts", {}, {},
332
+ "comment_posts", {}, { :post_id => {} }
333
+ )
334
+ Object.const_set(:Post, Class.new(ActiveRecord::Base))
335
+ Object.const_set(:CommentPost, Class.new(ActiveRecord::Base))
336
+ end
337
+
338
+ def prefix_both
339
+ create_tables(
340
+ "blog_page_posts", {}, {},
341
+ "blog_page_comments", {}, { :blog_page_post_id => {} }
342
+ )
343
+ Object.const_set(:BlogPagePost, Class.new(ActiveRecord::Base))
344
+ Object.const_set(:BlogPageComment, Class.new(ActiveRecord::Base))
345
+ end
346
+
347
+ it "should use concise association name for one prefix" do
348
+ with_associations_config(:auto_create => true, :concise_names => true) do
349
+ prefix_one
350
+ reflection = Post.reflect_on_association(:comments)
351
+ reflection.should_not be_nil
352
+ reflection.macro.should == :has_many
353
+ reflection.options[:class_name].should == "PostComment"
354
+ reflection.options[:foreign_key].should == "post_id"
355
+ end
356
+ end
357
+
358
+ it "should use concise association name for one suffix" do
359
+ with_associations_config(:auto_create => true, :concise_names => true) do
360
+ suffix_one
361
+ reflection = Post.reflect_on_association(:comments)
362
+ reflection.should_not be_nil
363
+ reflection.macro.should == :has_many
364
+ reflection.options[:class_name].should == "CommentPost"
365
+ reflection.options[:foreign_key].should == "post_id"
366
+ end
367
+ end
368
+
369
+ it "should use concise association name for shared prefixes" do
370
+ with_associations_config(:auto_create => true, :concise_names => true) do
371
+ prefix_both
372
+ reflection = BlogPagePost.reflect_on_association(:comments)
373
+ reflection.should_not be_nil
374
+ reflection.macro.should == :has_many
375
+ reflection.options[:class_name].should == "BlogPageComment"
376
+ reflection.options[:foreign_key].should == "blog_page_post_id"
377
+ end
378
+ end
379
+
380
+ it "should use full names and not concise names when so configured" do
381
+ with_associations_config(:auto_create => true, :concise_names => false) do
382
+ prefix_one
383
+ reflection = Post.reflect_on_association(:post_comments)
384
+ reflection.should_not be_nil
385
+ reflection.macro.should == :has_many
386
+ reflection.options[:class_name].should == "PostComment"
387
+ reflection.options[:foreign_key].should == "post_id"
388
+ reflection = Post.reflect_on_association(:comments)
389
+ reflection.should be_nil
390
+ end
391
+ end
392
+
393
+ it "should use concise names and not full names when so configured" do
394
+ with_associations_config(:auto_create => true, :concise_names => true) do
395
+ prefix_one
396
+ reflection = Post.reflect_on_association(:comments)
397
+ reflection.should_not be_nil
398
+ reflection.macro.should == :has_many
399
+ reflection.options[:class_name].should == "PostComment"
400
+ reflection.options[:foreign_key].should == "post_id"
401
+ reflection = Post.reflect_on_association(:post_comments)
402
+ reflection.should be_nil
403
+ end
404
+ end
405
+
406
+
407
+ end
408
+
409
+ context "with joins table" do
410
+ before(:each) do
411
+ create_tables(
412
+ "posts", {}, {},
413
+ "tags", {}, {},
414
+ "posts_tags", {:id => false}, { :post_id => {}, :tag_id => {}}
415
+ )
416
+ class Post < ActiveRecord::Base ; end
417
+ class Tag < ActiveRecord::Base ; end
418
+ end
419
+ it "should create has_and_belongs_to_many association" do
420
+ reflection = Post.reflect_on_association(:tags)
421
+ reflection.should_not be_nil
422
+ reflection.macro.should == :has_and_belongs_to_many
423
+ reflection.options[:class_name].should == "Tag"
424
+ reflection.options[:join_table].should == "posts_tags"
425
+ end
426
+ end
427
+
428
+ context "regarding existing methods" do
429
+ before(:each) do
430
+ create_tables(
431
+ "types", {}, {},
432
+ "posts", {}, {:type_id => {}}
433
+ )
434
+ end
435
+ it "should define association normally if no existing method is defined" do
436
+ class Type < ActiveRecord::Base ; end
437
+ Type.reflect_on_association(:posts).should_not be_nil # sanity check for this context
438
+ end
439
+ it "should not define association over existing public method" do
440
+ class Type < ActiveRecord::Base
441
+ def posts
442
+ :existing
443
+ end
444
+ end
445
+ Type.reflect_on_association(:posts).should be_nil
446
+ end
447
+ it "should not define association over existing private method" do
448
+ class Type < ActiveRecord::Base
449
+ private
450
+ def posts
451
+ :existing
452
+ end
453
+ end
454
+ Type.reflect_on_association(:posts).should be_nil
455
+ end
456
+ it "should define association :type over (deprecated) kernel method" do
457
+ class Post < ActiveRecord::Base ; end
458
+ Post.reflect_on_association(:type).should_not be_nil
459
+ end
460
+ it "should not define association :type over model method" do
461
+ class Post < ActiveRecord::Base
462
+ def type
463
+ :existing
464
+ end
465
+ end
466
+ Post.reflect_on_association(:type).should be_nil
467
+ end
468
+ end
469
+
470
+ context "regarding relations" do
471
+ before(:each) do
472
+ create_tables(
473
+ "posts", {}, {},
474
+ "comments", {}, { :post_id => {} }
475
+ )
476
+ class Post < ActiveRecord::Base ; end
477
+ class Comment < ActiveRecord::Base ; end
478
+ end
479
+
480
+ it "should define associations before needed by relation" do
481
+ Post.joins(:comments).all
482
+ expect { Post.joins(:comments).all }.should_not raise_error
483
+
484
+ end
485
+
486
+ end
487
+
488
+ protected
489
+
490
+ def with_fk_auto_create(value = true, &block)
491
+ save = SchemaPlus.config.foreign_keys.auto_create
492
+ begin
493
+ SchemaPlus.config.foreign_keys.auto_create = value
494
+ yield
495
+ ensure
496
+ SchemaPlus.config.foreign_keys.auto_create = save
497
+ end
498
+ end
499
+
500
+ def with_associations_auto_create(value, &block)
501
+ with_associations_config(:auto_create => value, &block)
502
+ end
503
+
504
+ def with_associations_config(opts, &block)
505
+ save = Hash[opts.keys.collect{|key| [key, SchemaPlus.config.associations.send(key)]}]
506
+ begin
507
+ SchemaPlus.config.associations.update_attributes(opts)
508
+ yield
509
+ ensure
510
+ SchemaPlus.config.associations.update_attributes(save)
511
+ end
512
+ end
513
+
514
+ def create_tables(*table_defs)
515
+ ActiveRecord::Migration.suppress_messages do
516
+ ActiveRecord::Base.connection.tables.each do |table|
517
+ ActiveRecord::Migration.drop_table table
518
+ end
519
+ table_defs.each_slice(3) do |table_name, opts, columns_with_options|
520
+ ActiveRecord::Migration.create_table table_name, opts do |t|
521
+ columns_with_options.each_pair do |column, options|
522
+ t.integer column, options
523
+ end
524
+ end
525
+ end
526
+ end
527
+ end
528
+
529
+ end