schema_plus 0.1.0.pre2 → 0.1.0.pre3
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.rdoc +8 -3
- data/lib/schema_plus.rb +4 -60
- data/lib/schema_plus/active_record/base.rb +0 -1
- data/lib/schema_plus/version.rb +1 -1
- data/schema_plus.gemspec.rails3.0 +2 -2
- data/spec/foreign_key_spec.rb +91 -73
- data/spec/spec_helper.rb +0 -2
- data/spec/validations_spec.rb +0 -2
- metadata +2 -5
- data/lib/schema_plus/active_record/associations.rb +0 -211
- data/spec/association_spec.rb +0 -529
data/README.rdoc
CHANGED
@@ -4,14 +4,18 @@
|
|
4
4
|
|
5
5
|
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.
|
6
6
|
|
7
|
-
For added rails DRYness see also the gems
|
7
|
+
For added rails DRYness see also the gems
|
8
|
+
{+schema_associations+}[http://rubygems.org/gems/schema_associations] and
|
9
|
+
{+schema_validations+}[http://rubygems.org/gems/schema_associations]
|
10
|
+
<b>IMPORTANT PRERELEASE NOTE: <i>schema_validations is not yet separate
|
11
|
+
gems, it's currently bundled in here. It's not documented yet though.</i></b>
|
8
12
|
|
9
13
|
== Compatibility
|
10
14
|
|
11
15
|
SchemaPlus supports all combinations of:
|
12
16
|
* rails 3.0 or 3.1
|
13
17
|
* MRI ruby 1.8.7 or 1.9.2
|
14
|
-
*
|
18
|
+
* PostgreSQL, MySQL (using mysql or mysql2 gem), or Sqlite3
|
15
19
|
|
16
20
|
== Installation
|
17
21
|
|
@@ -25,7 +29,8 @@ or in a Gemfile
|
|
25
29
|
|
26
30
|
== Features
|
27
31
|
|
28
|
-
Here some examples that show off the high points. For full details see the
|
32
|
+
Here some examples that show off the high points. For full details see the
|
33
|
+
{RDoc documentation}[http://rubydoc.info/gems/schema_plus].
|
29
34
|
|
30
35
|
=== Indexes
|
31
36
|
|
data/lib/schema_plus.rb
CHANGED
@@ -11,7 +11,6 @@ require 'schema_plus/active_record/connection_adapters/abstract_adapter'
|
|
11
11
|
require 'schema_plus/active_record/connection_adapters/column'
|
12
12
|
require 'schema_plus/active_record/connection_adapters/foreign_key_definition'
|
13
13
|
require 'schema_plus/active_record/connection_adapters/index_definition'
|
14
|
-
require 'schema_plus/active_record/associations'
|
15
14
|
require 'schema_plus/railtie' if defined?(Rails)
|
16
15
|
|
17
16
|
module SchemaPlus
|
@@ -26,7 +25,7 @@ module SchemaPlus
|
|
26
25
|
end
|
27
26
|
end
|
28
27
|
|
29
|
-
# This global configuation options for
|
28
|
+
# This global configuation options for SchemaPlus.
|
30
29
|
# Set them in +config/initializers/schema_plus.rb+ using:
|
31
30
|
#
|
32
31
|
# SchemaPlus.setup do |config|
|
@@ -34,7 +33,7 @@ module SchemaPlus
|
|
34
33
|
# end
|
35
34
|
#
|
36
35
|
# The options are grouped into subsets based on area of functionality.
|
37
|
-
# See Config::ForeignKeys, Config::
|
36
|
+
# See Config::ForeignKeys, Config::Validations
|
38
37
|
#
|
39
38
|
class Config < Valuable
|
40
39
|
|
@@ -81,63 +80,6 @@ module SchemaPlus
|
|
81
80
|
end
|
82
81
|
has_value :foreign_keys, :klass => ForeignKeys, :default => ForeignKeys.new
|
83
82
|
|
84
|
-
# This set of configuration options control SchemaPlus's automatic
|
85
|
-
# association behavior. Set them in
|
86
|
-
# +config/initializers/schema_plus.rb+ using:
|
87
|
-
#
|
88
|
-
# SchemaPlus.setup do |config|
|
89
|
-
# config.associations.auto_create = ...
|
90
|
-
# end
|
91
|
-
#
|
92
|
-
class Associations < Valuable
|
93
|
-
|
94
|
-
##
|
95
|
-
# :attr_accessor: auto_create
|
96
|
-
#
|
97
|
-
# Whether to automatically create associations based on foreign keys.
|
98
|
-
# Boolean, default is +true+.
|
99
|
-
has_value :auto_create, :klass => :boolean, :default => true
|
100
|
-
|
101
|
-
##
|
102
|
-
# :attr_accessor: concise_names
|
103
|
-
#
|
104
|
-
# Whether to use concise naming (strip out common prefixes from class names).
|
105
|
-
# Boolean, default is +true+.
|
106
|
-
has_value :concise_names, :klass => :boolean, :default => true
|
107
|
-
|
108
|
-
##
|
109
|
-
# :attr_accessor: except
|
110
|
-
#
|
111
|
-
# List of association names to exclude from automatic creation.
|
112
|
-
# Value is a single name, an array of names, or +nil+. Default is +nil+.
|
113
|
-
has_value :except, :default => nil
|
114
|
-
|
115
|
-
##
|
116
|
-
# :attr_accessor: only
|
117
|
-
#
|
118
|
-
# List of association names to include in automatic creation.
|
119
|
-
# Value is a single name, and array of names, or +nil+. Default is +nil+.
|
120
|
-
has_value :only, :default => nil
|
121
|
-
|
122
|
-
##
|
123
|
-
# :attr_accessor: except_type
|
124
|
-
#
|
125
|
-
# List of association types to exclude from automatic creation.
|
126
|
-
# Value is one or an array of +:belongs_to+, +:has_many+, +:has_one+, and/or
|
127
|
-
# +:has_and_belongs_to_many+, or +nil+. Default is +nil+.
|
128
|
-
has_value :except_type, :default => nil
|
129
|
-
|
130
|
-
##
|
131
|
-
# :attr_accessor: only_type
|
132
|
-
#
|
133
|
-
# List of association types to include from automatic creation.
|
134
|
-
# Value is one or an array of +:belongs_to+, +:has_many+, +:has_one+, and/or
|
135
|
-
# +:has_and_belongs_to_many+, or +nil+. Default is +nil+.
|
136
|
-
has_value :only_type, :default => nil
|
137
|
-
|
138
|
-
end
|
139
|
-
has_value :associations, :klass => Associations, :default => Associations.new
|
140
|
-
|
141
83
|
# This set of configuration options control SchemaPlus's automatic
|
142
84
|
# validations behavior. Set them in
|
143
85
|
# +config/initializers/schema_plus.rb+ using:
|
@@ -246,3 +188,5 @@ module SchemaPlus
|
|
246
188
|
end
|
247
189
|
|
248
190
|
end
|
191
|
+
|
192
|
+
SchemaPlus.insert unless defined? Rails::Railtie
|
data/lib/schema_plus/version.rb
CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.authors = ["Ronen Barzel", "Michał Łomnicki"]
|
10
10
|
s.email = ["ronen@barzel.org", "michal.lomnicki@gmail.com"]
|
11
11
|
s.homepage = "https://github.com/ronen/schema_plus"
|
12
|
-
s.summary = "
|
13
|
-
s.description = "SchemaPlus is an ActiveRecord extension that provides
|
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
14
|
|
15
15
|
s.rubyforge_project = "schema_plus"
|
16
16
|
|
data/spec/foreign_key_spec.rb
CHANGED
@@ -6,118 +6,136 @@ require 'models/comment'
|
|
6
6
|
|
7
7
|
describe "Foreign Key" do
|
8
8
|
|
9
|
-
before(:all) do
|
10
|
-
load_core_schema
|
11
|
-
end
|
12
|
-
|
13
9
|
let(:migration) { ::ActiveRecord::Migration }
|
14
10
|
|
15
|
-
|
11
|
+
context "created with table" do
|
12
|
+
before(:all) do
|
13
|
+
load_auto_schema
|
14
|
+
end
|
16
15
|
|
17
|
-
it "
|
18
|
-
|
19
|
-
add_foreign_key(:posts, :author_id, :users, :id, :on_update => :cascade, :on_delete => :restrict)
|
20
|
-
}.should raise_error(NotImplementedError)
|
16
|
+
it "should report foreign key constraints" do
|
17
|
+
Comment.foreign_keys.collect(&:column_names).flatten.should == [ "user_id" ]
|
21
18
|
end
|
22
19
|
|
23
|
-
it "
|
24
|
-
|
25
|
-
remove_foreign_key(:posts, "dummy")
|
26
|
-
}.should raise_error(NotImplementedError)
|
20
|
+
it "should report reverse foreign key constraints" do
|
21
|
+
User.reverse_foreign_keys.collect(&:column_names).flatten.should == [ "user_id" ]
|
27
22
|
end
|
28
23
|
|
29
|
-
|
24
|
+
end
|
30
25
|
|
31
|
-
|
26
|
+
context "modification" do
|
32
27
|
|
33
|
-
|
34
|
-
|
35
|
-
|
28
|
+
before(:all) do
|
29
|
+
load_core_schema
|
30
|
+
end
|
36
31
|
|
37
|
-
|
38
|
-
fk = Post.foreign_keys.detect { |fk| fk.column_names == %w[author_id] }
|
39
|
-
remove_foreign_key(:posts, fk.name)
|
40
|
-
end
|
32
|
+
if SchemaPlusHelpers.sqlite3?
|
41
33
|
|
42
|
-
it "
|
43
|
-
|
34
|
+
it "raises an exception when attempting to add" do
|
35
|
+
expect {
|
36
|
+
add_foreign_key(:posts, :author_id, :users, :id, :on_update => :cascade, :on_delete => :restrict)
|
37
|
+
}.should raise_error(NotImplementedError)
|
44
38
|
end
|
45
39
|
|
46
|
-
it "
|
47
|
-
|
40
|
+
it "raises an exception when attempting to remove" do
|
41
|
+
expect {
|
42
|
+
remove_foreign_key(:posts, "dummy")
|
43
|
+
}.should raise_error(NotImplementedError)
|
48
44
|
end
|
49
45
|
|
50
|
-
|
51
|
-
Post.should reference(:users).on_delete(:restrict)
|
52
|
-
end
|
46
|
+
else
|
53
47
|
|
54
|
-
|
55
|
-
Post.foreign_keys.collect(&:column_names).should include(%w[author_id])
|
56
|
-
end
|
48
|
+
context "when is added", "posts(author_id)" do
|
57
49
|
|
58
|
-
|
59
|
-
|
60
|
-
|
50
|
+
before(:each) do
|
51
|
+
add_foreign_key(:posts, :author_id, :users, :id, :on_update => :cascade, :on_delete => :restrict)
|
52
|
+
end
|
61
53
|
|
62
|
-
|
54
|
+
after(:each) do
|
55
|
+
fk = Post.foreign_keys.detect { |fk| fk.column_names == %w[author_id] }
|
56
|
+
remove_foreign_key(:posts, fk.name)
|
57
|
+
end
|
63
58
|
|
64
|
-
|
59
|
+
it "references users(id)" do
|
60
|
+
Post.should reference(:users, :id).on(:author_id)
|
61
|
+
end
|
65
62
|
|
66
|
-
|
63
|
+
it "cascades on update" do
|
64
|
+
Post.should reference(:users).on_update(:cascade)
|
65
|
+
end
|
67
66
|
|
68
|
-
|
69
|
-
|
70
|
-
|
67
|
+
it "restricts on delete" do
|
68
|
+
Post.should reference(:users).on_delete(:restrict)
|
69
|
+
end
|
71
70
|
|
72
|
-
|
73
|
-
|
74
|
-
|
71
|
+
it "is available in Post.foreign_keys" do
|
72
|
+
Post.foreign_keys.collect(&:column_names).should include(%w[author_id])
|
73
|
+
end
|
75
74
|
|
76
|
-
|
77
|
-
|
78
|
-
|
75
|
+
it "is available in User.reverse_foreign_keys" do
|
76
|
+
User.reverse_foreign_keys.collect(&:column_names).should include(%w[author_id])
|
77
|
+
end
|
79
78
|
|
80
|
-
it "is no longer available in Post.foreign_keys" do
|
81
|
-
Comment.foreign_keys.collect(&:column_names).should_not include(%w[post_id])
|
82
79
|
end
|
83
80
|
|
84
|
-
|
85
|
-
Post.reverse_foreign_keys.collect(&:column_names).should_not include(%w[post_id])
|
86
|
-
end
|
81
|
+
context "when is dropped", "comments(post_id)" do
|
87
82
|
|
88
|
-
|
83
|
+
let(:foreign_key_name) { fk = Comment.foreign_keys.detect { |definition| definition.column_names == %w[post_id] } and fk.name }
|
84
|
+
|
85
|
+
before(:each) do
|
86
|
+
remove_foreign_key(:comments, foreign_key_name)
|
87
|
+
end
|
88
|
+
|
89
|
+
after(:each) do
|
90
|
+
add_foreign_key(:comments, :post_id, :posts, :id)
|
91
|
+
end
|
89
92
|
|
90
|
-
|
93
|
+
it "doesn't reference posts(id)" do
|
94
|
+
Comment.should_not reference(:posts).on(:post_id)
|
95
|
+
end
|
91
96
|
|
92
|
-
|
97
|
+
it "is no longer available in Post.foreign_keys" do
|
98
|
+
Comment.foreign_keys.collect(&:column_names).should_not include(%w[post_id])
|
99
|
+
end
|
100
|
+
|
101
|
+
it "is no longer available in User.reverse_foreign_keys" do
|
102
|
+
Post.reverse_foreign_keys.collect(&:column_names).should_not include(%w[post_id])
|
103
|
+
end
|
93
104
|
|
94
|
-
it "should remove foreign keys" do
|
95
|
-
remove_foreign_key(:comments, foreign_key_name)
|
96
|
-
Post.reverse_foreign_keys.collect { |fk| fk.column_names == %w[post_id] && fk.table_name == "comments" }.should be_empty
|
97
105
|
end
|
98
106
|
|
99
|
-
|
107
|
+
context "when referencing column and column is removed" do
|
100
108
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
end
|
109
|
+
let(:foreign_key_name) { Comment.foreign_keys.detect { |definition| definition.column_names == %w[post_id] }.name }
|
110
|
+
|
111
|
+
it "should remove foreign keys" do
|
112
|
+
remove_foreign_key(:comments, foreign_key_name)
|
113
|
+
Post.reverse_foreign_keys.collect { |fk| fk.column_names == %w[post_id] && fk.table_name == "comments" }.should be_empty
|
107
114
|
end
|
115
|
+
|
108
116
|
end
|
109
117
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
migration.
|
114
|
-
|
115
|
-
|
116
|
-
|
118
|
+
context "when table name is a rerved word" do
|
119
|
+
before(:each) do
|
120
|
+
migration.suppress_messages do
|
121
|
+
migration.create_table :references, :force => true do |t|
|
122
|
+
t.integer :post_id
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
it "can add, detect, and remove a foreign key without error" do
|
128
|
+
migration.suppress_messages do
|
129
|
+
expect {
|
130
|
+
migration.add_foreign_key(:references, :post_id, :posts, :id)
|
131
|
+
foreign_key = migration.foreign_keys(:references).detect{|definition| definition.column_names == ["post_id"]}
|
132
|
+
migration.remove_foreign_key(:references, foreign_key.name)
|
133
|
+
}.should_not raise_error
|
134
|
+
end
|
117
135
|
end
|
118
136
|
end
|
119
|
-
end
|
120
137
|
|
138
|
+
end
|
121
139
|
end
|
122
140
|
|
123
141
|
protected
|
data/spec/spec_helper.rb
CHANGED
data/spec/validations_spec.rb
CHANGED
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: schema_plus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease: 6
|
5
|
-
version: 0.1.0.
|
5
|
+
version: 0.1.0.pre3
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Ronen Barzel
|
@@ -11,7 +11,7 @@ autorequire:
|
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
13
|
|
14
|
-
date: 2011-07-
|
14
|
+
date: 2011-07-17 00:00:00 -07:00
|
15
15
|
default_executable:
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
@@ -164,7 +164,6 @@ files:
|
|
164
164
|
- Rakefile
|
165
165
|
- init.rb
|
166
166
|
- lib/schema_plus.rb
|
167
|
-
- lib/schema_plus/active_record/associations.rb
|
168
167
|
- lib/schema_plus/active_record/base.rb
|
169
168
|
- lib/schema_plus/active_record/connection_adapters/abstract_adapter.rb
|
170
169
|
- lib/schema_plus/active_record/connection_adapters/column.rb
|
@@ -184,7 +183,6 @@ files:
|
|
184
183
|
- schema_plus.gemspec
|
185
184
|
- schema_plus.gemspec.rails3.0
|
186
185
|
- schema_plus.gemspec.rails3.1
|
187
|
-
- spec/association_spec.rb
|
188
186
|
- spec/connections/mysql/connection.rb
|
189
187
|
- spec/connections/mysql2/connection.rb
|
190
188
|
- spec/connections/postgresql/connection.rb
|
@@ -240,7 +238,6 @@ signing_key:
|
|
240
238
|
specification_version: 3
|
241
239
|
summary: Enhances ActiveRecord schema mechanism, including more DRY index creation and support for foreign key constraints and views.
|
242
240
|
test_files:
|
243
|
-
- spec/association_spec.rb
|
244
241
|
- spec/connections/mysql/connection.rb
|
245
242
|
- spec/connections/mysql2/connection.rb
|
246
243
|
- spec/connections/postgresql/connection.rb
|
@@ -1,211 +0,0 @@
|
|
1
|
-
require 'ostruct'
|
2
|
-
|
3
|
-
module SchemaPlus
|
4
|
-
module ActiveRecord
|
5
|
-
module Associations
|
6
|
-
|
7
|
-
module Relation #:nodoc:
|
8
|
-
def self.included(base)
|
9
|
-
base.alias_method_chain :initialize, :schema_plus
|
10
|
-
end
|
11
|
-
|
12
|
-
def initialize_with_schema_plus(klass, *args)
|
13
|
-
klass.send :_load_schema_plus_associations
|
14
|
-
initialize_without_schema_plus(klass, *args)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.extended(base) #:nodoc:
|
19
|
-
class << base
|
20
|
-
alias_method_chain :reflect_on_association, :schema_plus
|
21
|
-
alias_method_chain :reflect_on_all_associations, :schema_plus
|
22
|
-
end
|
23
|
-
::ActiveRecord::Relation.send :include, Relation
|
24
|
-
end
|
25
|
-
|
26
|
-
def reflect_on_association_with_schema_plus(*args) #:nodoc:
|
27
|
-
_load_schema_plus_associations
|
28
|
-
reflect_on_association_without_schema_plus(*args)
|
29
|
-
end
|
30
|
-
|
31
|
-
def reflect_on_all_associations_with_schema_plus(*args) #:nodoc:
|
32
|
-
_load_schema_plus_associations
|
33
|
-
reflect_on_all_associations_without_schema_plus(*args)
|
34
|
-
end
|
35
|
-
|
36
|
-
def define_attribute_methods(*args) #:nodoc:
|
37
|
-
super
|
38
|
-
_load_schema_plus_associations
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def _load_schema_plus_associations #:nodoc:
|
44
|
-
return if @schema_plus_associations_loaded
|
45
|
-
@schema_plus_associations_loaded = true
|
46
|
-
return unless schema_plus_config.associations.auto_create?
|
47
|
-
|
48
|
-
reverse_foreign_keys.each do | foreign_key |
|
49
|
-
if foreign_key.table_name =~ /^#{table_name}_(.*)$/ || foreign_key.table_name =~ /^(.*)_#{table_name}$/
|
50
|
-
other_table = $1
|
51
|
-
if other_table == other_table.pluralize and connection.columns(foreign_key.table_name).any?{|col| col.name == "#{other_table.singularize}_id"}
|
52
|
-
_define_association(:has_and_belongs_to_many, foreign_key, other_table)
|
53
|
-
else
|
54
|
-
_define_association(:has_one_or_many, foreign_key)
|
55
|
-
end
|
56
|
-
else
|
57
|
-
_define_association(:has_one_or_many, foreign_key)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
foreign_keys.each do | foreign_key |
|
62
|
-
_define_association(:belongs_to, foreign_key)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def _define_association(macro, fk, referencing_table_name = nil) #:nodoc:
|
67
|
-
return unless fk.column_names.size == 1
|
68
|
-
|
69
|
-
referencing_table_name ||= fk.table_name
|
70
|
-
|
71
|
-
column_name = fk.column_names.first
|
72
|
-
reference_name = column_name.sub(/_id$/, '')
|
73
|
-
references_name = fk.references_table_name.singularize
|
74
|
-
referencing_name = referencing_table_name.singularize
|
75
|
-
|
76
|
-
references_class_name = references_name.classify
|
77
|
-
referencing_class_name = referencing_name.classify
|
78
|
-
|
79
|
-
references_concise = _concise_name(references_name, referencing_name)
|
80
|
-
referencing_concise = _concise_name(referencing_name, references_name)
|
81
|
-
|
82
|
-
case reference_name
|
83
|
-
when 'parent'
|
84
|
-
belongs_to = 'parent'
|
85
|
-
belongs_to_concise = 'parent'
|
86
|
-
|
87
|
-
has_one = 'child'
|
88
|
-
has_one_concise = 'child'
|
89
|
-
|
90
|
-
has_many = 'children'
|
91
|
-
has_many_concise = 'children'
|
92
|
-
|
93
|
-
when references_name
|
94
|
-
belongs_to = references_name
|
95
|
-
belongs_to_concise = references_concise
|
96
|
-
|
97
|
-
has_one = referencing_name
|
98
|
-
has_one_concise = referencing_concise
|
99
|
-
|
100
|
-
has_many = referencing_name.pluralize
|
101
|
-
has_many_concise = referencing_concise.pluralize
|
102
|
-
|
103
|
-
when /(.*)_#{references_name}$/, /(.*)_#{references_concise}$/
|
104
|
-
label = $1
|
105
|
-
belongs_to = "#{label}_#{references_name}"
|
106
|
-
belongs_to_concise = "#{label}_#{references_concise}"
|
107
|
-
|
108
|
-
has_one = "#{referencing_name}_as_#{label}"
|
109
|
-
has_one_concise = "#{referencing_concise}_as_#{label}"
|
110
|
-
|
111
|
-
has_many = "#{referencing_name.pluralize}_as_#{label}"
|
112
|
-
has_many_concise = "#{referencing_concise.pluralize}_as_#{label}"
|
113
|
-
|
114
|
-
when /^#{references_name}_(.*)$/, /^#{references_concise}_(.*)$/
|
115
|
-
label = $1
|
116
|
-
belongs_to = "#{references_name}_#{label}"
|
117
|
-
belongs_to_concise = "#{references_concise}_#{label}"
|
118
|
-
|
119
|
-
has_one = "#{referencing_name}_as_#{label}"
|
120
|
-
has_one_concise = "#{referencing_concise}_as_#{label}"
|
121
|
-
|
122
|
-
has_many = "#{referencing_name.pluralize}_as_#{label}"
|
123
|
-
has_many_concise = "#{referencing_concise.pluralize}_as_#{label}"
|
124
|
-
|
125
|
-
else
|
126
|
-
belongs_to = reference_name
|
127
|
-
belongs_to_concise = reference_name
|
128
|
-
|
129
|
-
has_one = "#{referencing_name}_as_#{reference_name}"
|
130
|
-
has_one_concise = "#{referencing_concise}_as_#{reference_name}"
|
131
|
-
|
132
|
-
has_many = "#{referencing_name.pluralize}_as_#{reference_name}"
|
133
|
-
has_many_concise = "#{referencing_concise.pluralize}_as_#{reference_name}"
|
134
|
-
end
|
135
|
-
|
136
|
-
case macro
|
137
|
-
when :has_and_belongs_to_many
|
138
|
-
name = has_many
|
139
|
-
name_concise = has_many_concise
|
140
|
-
opts = {:class_name => referencing_class_name, :join_table => fk.table_name, :foreign_key => column_name}
|
141
|
-
when :belongs_to
|
142
|
-
name = belongs_to
|
143
|
-
name_concise = belongs_to_concise
|
144
|
-
opts = {:class_name => references_class_name, :foreign_key => column_name}
|
145
|
-
when :has_one_or_many
|
146
|
-
opts = {:class_name => referencing_class_name, :foreign_key => column_name}
|
147
|
-
# use connection.indexes and connection.colums rather than class
|
148
|
-
# methods of the referencing class because using the class
|
149
|
-
# methods would require getting the class -- which might trigger
|
150
|
-
# an autoload which could start some recursion making things much
|
151
|
-
# harder to debug.
|
152
|
-
if connection.indexes(referencing_table_name, "#{referencing_table_name} Indexes").any?{|index| index.unique && index.columns == [column_name]}
|
153
|
-
macro = :has_one
|
154
|
-
name = has_one
|
155
|
-
name_concise = has_one_concise
|
156
|
-
else
|
157
|
-
macro = :has_many
|
158
|
-
name = has_many
|
159
|
-
name_concise = has_many_concise
|
160
|
-
if connection.columns(referencing_table_name, "#{referencing_table_name} Columns").any?{ |col| col.name == 'position' }
|
161
|
-
opts[:order] = :position
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
name = name_concise if _use_concise_name?
|
166
|
-
name = name.to_sym
|
167
|
-
if (_filter_association(macro, name) && !_method_exists?(name))
|
168
|
-
logger.info "SchemaPlus associations: #{self.name || self.table_name.classify}.#{macro} #{name.inspect}, #{opts.inspect[1...-1]}"
|
169
|
-
send macro, name, opts.dup
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
def _concise_name(string, other) #:nodoc:
|
174
|
-
case
|
175
|
-
when string =~ /^#{other}_(.*)$/ then $1
|
176
|
-
when string =~ /(.*)_#{other}$/ then $1
|
177
|
-
when leader = _common_leader(string,other) then string[leader.length, string.length-leader.length]
|
178
|
-
else string
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
def _common_leader(string, other) #:nodoc:
|
183
|
-
leader = nil
|
184
|
-
other.split('_').each do |part|
|
185
|
-
test = "#{leader}#{part}_"
|
186
|
-
break unless string.start_with? test
|
187
|
-
leader = test
|
188
|
-
end
|
189
|
-
return leader
|
190
|
-
end
|
191
|
-
|
192
|
-
def _use_concise_name? #:nodoc:
|
193
|
-
schema_plus_config.associations.concise_names?
|
194
|
-
end
|
195
|
-
|
196
|
-
def _filter_association(macro, name) #:nodoc:
|
197
|
-
config = schema_plus_config.associations
|
198
|
-
return false if config.only and not Array.wrap(config.only).include?(name)
|
199
|
-
return false if config.except and Array.wrap(config.except).include?(name)
|
200
|
-
return false if config.only_type and not Array.wrap(config.only_type).include?(macro)
|
201
|
-
return false if config.except_type and Array.wrap(config.except_type).include?(macro)
|
202
|
-
return true
|
203
|
-
end
|
204
|
-
|
205
|
-
def _method_exists?(name) #:nodoc:
|
206
|
-
method_defined?(name) || private_method_defined?(name) and not (name == :type && [Object, Kernel].include?(instance_method(:type).owner))
|
207
|
-
end
|
208
|
-
|
209
|
-
end
|
210
|
-
end
|
211
|
-
end
|
data/spec/association_spec.rb
DELETED
@@ -1,529 +0,0 @@
|
|
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
|