schema_plus 0.1.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +25 -0
- data/Gemfile +3 -0
- data/MIT-LICENSE +25 -0
- data/README.rdoc +147 -0
- data/Rakefile +70 -0
- data/init.rb +1 -0
- data/lib/schema_plus/active_record/associations.rb +211 -0
- data/lib/schema_plus/active_record/base.rb +81 -0
- data/lib/schema_plus/active_record/connection_adapters/abstract_adapter.rb +96 -0
- data/lib/schema_plus/active_record/connection_adapters/column.rb +55 -0
- data/lib/schema_plus/active_record/connection_adapters/foreign_key_definition.rb +115 -0
- data/lib/schema_plus/active_record/connection_adapters/index_definition.rb +51 -0
- data/lib/schema_plus/active_record/connection_adapters/mysql_adapter.rb +111 -0
- data/lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb +163 -0
- data/lib/schema_plus/active_record/connection_adapters/schema_statements.rb +39 -0
- data/lib/schema_plus/active_record/connection_adapters/sqlite3_adapter.rb +78 -0
- data/lib/schema_plus/active_record/connection_adapters/table_definition.rb +130 -0
- data/lib/schema_plus/active_record/migration.rb +220 -0
- data/lib/schema_plus/active_record/schema.rb +27 -0
- data/lib/schema_plus/active_record/schema_dumper.rb +122 -0
- data/lib/schema_plus/active_record/validations.rb +139 -0
- data/lib/schema_plus/railtie.rb +12 -0
- data/lib/schema_plus/version.rb +3 -0
- data/lib/schema_plus.rb +248 -0
- data/schema_plus.gemspec +37 -0
- data/schema_plus.gemspec.rails3.0 +36 -0
- data/schema_plus.gemspec.rails3.1 +36 -0
- data/spec/association_spec.rb +529 -0
- data/spec/connections/mysql/connection.rb +18 -0
- data/spec/connections/mysql2/connection.rb +18 -0
- data/spec/connections/postgresql/connection.rb +15 -0
- data/spec/connections/sqlite3/connection.rb +14 -0
- data/spec/foreign_key_definition_spec.rb +23 -0
- data/spec/foreign_key_spec.rb +142 -0
- data/spec/index_definition_spec.rb +139 -0
- data/spec/index_spec.rb +71 -0
- data/spec/migration_spec.rb +405 -0
- data/spec/models/comment.rb +2 -0
- data/spec/models/post.rb +2 -0
- data/spec/models/user.rb +2 -0
- data/spec/references_spec.rb +78 -0
- data/spec/schema/auto_schema.rb +23 -0
- data/spec/schema/core_schema.rb +21 -0
- data/spec/schema_dumper_spec.rb +167 -0
- data/spec/schema_spec.rb +71 -0
- data/spec/spec_helper.rb +59 -0
- data/spec/support/extensions/active_model.rb +13 -0
- data/spec/support/helpers.rb +16 -0
- data/spec/support/matchers/automatic_foreign_key_matchers.rb +2 -0
- data/spec/support/matchers/have_index.rb +52 -0
- data/spec/support/matchers/reference.rb +66 -0
- data/spec/support/reference.rb +66 -0
- data/spec/validations_spec.rb +294 -0
- data/spec/views_spec.rb +140 -0
- metadata +269 -0
data/schema_plus.gemspec
ADDED
@@ -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
|