inverse_of 0.0.1
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/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +72 -0
- data/Rakefile +38 -0
- data/VERSION +1 -0
- data/install.rb +1 -0
- data/inverse_of.gemspec +76 -0
- data/lib/inverse_of.rb +293 -0
- data/rails/init.rb +1 -0
- data/tasks/inverse_of_tasks.rake +4 -0
- data/test/cases/helper.rb +28 -0
- data/test/cases/inverse_associations_test.rb +567 -0
- data/test/fixtures/clubs.yml +6 -0
- data/test/fixtures/faces.yml +11 -0
- data/test/fixtures/interests.yml +33 -0
- data/test/fixtures/men.yml +5 -0
- data/test/fixtures/sponsors.yml +9 -0
- data/test/fixtures/zines.yml +5 -0
- data/test/models/club.rb +13 -0
- data/test/models/face.rb +7 -0
- data/test/models/interest.rb +5 -0
- data/test/models/man.rb +9 -0
- data/test/models/sponsor.rb +4 -0
- data/test/models/zine.rb +3 -0
- data/test/schema/schema.rb +32 -0
- data/uninstall.rb +1 -0
- metadata +91 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 George Ogata
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 [name of plugin creator]
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# Inverse Of
|
2
|
+
|
3
|
+
Backport of ActiveRecord 2.3.6's inverse associations feature.
|
4
|
+
|
5
|
+
class Parent < ActiveRecord::Base
|
6
|
+
has_one :child, :inverse_of => :parent
|
7
|
+
end
|
8
|
+
|
9
|
+
class Child < ActiveRecord::Base
|
10
|
+
belongs_to :parent, :inverse_of => :child
|
11
|
+
end
|
12
|
+
|
13
|
+
Now:
|
14
|
+
|
15
|
+
parent = Parent.first
|
16
|
+
parent.child.parent.equal?(parent) # true
|
17
|
+
|
18
|
+
Without inverse associations, the last line is false. Although
|
19
|
+
ActiveRecord does perform database query caching, you still suffer the
|
20
|
+
overhead of creating unnecessary ActiveRecord objects.
|
21
|
+
|
22
|
+
Inverse associations are also necessary to support validations on the
|
23
|
+
parent which must fire before the parent is saved. For example:
|
24
|
+
|
25
|
+
class Parent < ActiveRecord::Base
|
26
|
+
has_one :child
|
27
|
+
accepts_nested_attributes_for :child
|
28
|
+
end
|
29
|
+
|
30
|
+
class Child < ActiveRecord::Base
|
31
|
+
belongs_to :parent
|
32
|
+
validates_presence_of :parent_id
|
33
|
+
end
|
34
|
+
|
35
|
+
Parent.new(:child_attributes => {:name => 'child name'}).valid?
|
36
|
+
|
37
|
+
Here, the parent object fails validation, because the parent_id is not
|
38
|
+
set yet. If inverses are declared, and the validation runs on the
|
39
|
+
association rather than the ID, the validation passes.
|
40
|
+
|
41
|
+
class Parent < ActiveRecord::Base
|
42
|
+
has_one :child, :inverse_of => :parent
|
43
|
+
accepts_nested_attributes_for :child
|
44
|
+
end
|
45
|
+
|
46
|
+
class Child < ActiveRecord::Base
|
47
|
+
belongs_to :parent
|
48
|
+
validates_presence_of :parent
|
49
|
+
end
|
50
|
+
|
51
|
+
Note that running the Child validations will now require loading the
|
52
|
+
Parent if it's not already loaded. On the upside, it will now also
|
53
|
+
validate that the parent_id actually points to an existing record.
|
54
|
+
However, you may wish to define a custom validation to check only the
|
55
|
+
ID if the association is not set to avoid the extra database hit.
|
56
|
+
|
57
|
+
Inverse Of supports `has_one`, `has_many`, and `belongs_to`
|
58
|
+
associations, including polymorphic `belongs_to`. Behavior should
|
59
|
+
exactly match Rails 2.3.6; please file any differences
|
60
|
+
[as a bug](http://github.com/oggy/inverse_of/issues).
|
61
|
+
|
62
|
+
## Contributing
|
63
|
+
|
64
|
+
* Bug reports: http://github.com/oggy/inverse_of/issues
|
65
|
+
* Source: http://github.com/oggy/inverse_of
|
66
|
+
* Patches: Fork on Github, send pull request.
|
67
|
+
* Ensure patch includes tests.
|
68
|
+
* Leave the version alone, or bump it in a separate commit.
|
69
|
+
|
70
|
+
## Copyright
|
71
|
+
|
72
|
+
Copyright (c) 2009-2010 George Ogata, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "inverse_of"
|
8
|
+
gem.summary = "Backport of ActiveRecord 2.3.6's inverse associations."
|
9
|
+
gem.description = "Backport of ActiveRecord 2.3.6's inverse associations."
|
10
|
+
gem.email = "george.ogata@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/oggy/inverse_of"
|
12
|
+
gem.authors = ["George Ogata"]
|
13
|
+
end
|
14
|
+
Jeweler::GemcutterTasks.new
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
|
21
|
+
desc "Run the test/unit tests for inverse associations, backported from ActiveRecord 2.3.6."
|
22
|
+
Rake::TestTask.new(:test => :check_dependencies) do |test|
|
23
|
+
test.libs << 'lib' << 'test'
|
24
|
+
test.pattern = 'test/**/*_test.rb'
|
25
|
+
test.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
task :default => :test
|
29
|
+
|
30
|
+
require 'rake/rdoctask'
|
31
|
+
Rake::RDocTask.new do |rdoc|
|
32
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
33
|
+
|
34
|
+
rdoc.rdoc_dir = 'rdoc'
|
35
|
+
rdoc.title = "inverse_of #{version}"
|
36
|
+
rdoc.rdoc_files.include('README*')
|
37
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
38
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Install hook code here
|
data/inverse_of.gemspec
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{inverse_of}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["George Ogata"]
|
12
|
+
s.date = %q{2010-02-05}
|
13
|
+
s.description = %q{Backport of ActiveRecord 2.3.6's inverse associations.}
|
14
|
+
s.email = %q{george.ogata@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.markdown"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"MIT-LICENSE",
|
24
|
+
"README.markdown",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"install.rb",
|
28
|
+
"inverse_of.gemspec",
|
29
|
+
"lib/inverse_of.rb",
|
30
|
+
"rails/init.rb",
|
31
|
+
"tasks/inverse_of_tasks.rake",
|
32
|
+
"test/cases/helper.rb",
|
33
|
+
"test/cases/inverse_associations_test.rb",
|
34
|
+
"test/fixtures/clubs.yml",
|
35
|
+
"test/fixtures/faces.yml",
|
36
|
+
"test/fixtures/interests.yml",
|
37
|
+
"test/fixtures/men.yml",
|
38
|
+
"test/fixtures/sponsors.yml",
|
39
|
+
"test/fixtures/zines.yml",
|
40
|
+
"test/models/club.rb",
|
41
|
+
"test/models/face.rb",
|
42
|
+
"test/models/interest.rb",
|
43
|
+
"test/models/man.rb",
|
44
|
+
"test/models/sponsor.rb",
|
45
|
+
"test/models/zine.rb",
|
46
|
+
"test/schema/schema.rb",
|
47
|
+
"uninstall.rb"
|
48
|
+
]
|
49
|
+
s.homepage = %q{http://github.com/oggy/inverse_of}
|
50
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
51
|
+
s.require_paths = ["lib"]
|
52
|
+
s.rubygems_version = %q{1.3.5}
|
53
|
+
s.summary = %q{Backport of ActiveRecord 2.3.6's inverse associations.}
|
54
|
+
s.test_files = [
|
55
|
+
"test/cases/helper.rb",
|
56
|
+
"test/cases/inverse_associations_test.rb",
|
57
|
+
"test/models/club.rb",
|
58
|
+
"test/models/face.rb",
|
59
|
+
"test/models/interest.rb",
|
60
|
+
"test/models/man.rb",
|
61
|
+
"test/models/sponsor.rb",
|
62
|
+
"test/models/zine.rb",
|
63
|
+
"test/schema/schema.rb"
|
64
|
+
]
|
65
|
+
|
66
|
+
if s.respond_to? :specification_version then
|
67
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
68
|
+
s.specification_version = 3
|
69
|
+
|
70
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
71
|
+
else
|
72
|
+
end
|
73
|
+
else
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
data/lib/inverse_of.rb
ADDED
@@ -0,0 +1,293 @@
|
|
1
|
+
if ([ActiveRecord::VERSION::MAJOR, ActiveRecord::VERSION::MINOR, ActiveRecord::VERSION::TINY] <=> [2, 3, 6]) < 0
|
2
|
+
module InverseOf
|
3
|
+
class InverseOfAssociationNotFoundError < ActiveRecord::ActiveRecordError #:nodoc:
|
4
|
+
def initialize(reflection, associated_class = nil)
|
5
|
+
super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module Reflection
|
10
|
+
def self.included(base)
|
11
|
+
base::AssociationReflection.send :include, AssociationReflection
|
12
|
+
base::ThroughReflection.send :include, ThroughReflection
|
13
|
+
end
|
14
|
+
|
15
|
+
module AssociationReflection
|
16
|
+
def self.included(base)
|
17
|
+
base.alias_method_chain :check_validity!, :inverse_of
|
18
|
+
end
|
19
|
+
|
20
|
+
def check_validity_with_inverse_of!
|
21
|
+
check_validity_of_inverse!
|
22
|
+
check_validity_without_inverse_of!
|
23
|
+
end
|
24
|
+
|
25
|
+
def check_validity_of_inverse!
|
26
|
+
unless options[:polymorphic]
|
27
|
+
if has_inverse? && inverse_of.nil?
|
28
|
+
raise InverseOfAssociationNotFoundError.new(self)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def has_inverse?
|
34
|
+
!@options[:inverse_of].nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
def inverse_of
|
38
|
+
if has_inverse?
|
39
|
+
@inverse_of ||= klass.reflect_on_association(options[:inverse_of])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def polymorphic_inverse_of(associated_class)
|
44
|
+
if has_inverse?
|
45
|
+
if inverse_relationship = associated_class.reflect_on_association(options[:inverse_of])
|
46
|
+
inverse_relationship
|
47
|
+
else
|
48
|
+
raise InverseOfAssociationNotFoundError.new(self, associated_class)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
module ThroughReflection
|
55
|
+
def self.included(base)
|
56
|
+
base.alias_method_chain :check_validity!, :inverse_of
|
57
|
+
end
|
58
|
+
|
59
|
+
def check_validity_with_inverse_of!
|
60
|
+
check_validity_of_inverse!
|
61
|
+
check_validity_without_inverse_of!
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
module Associations
|
67
|
+
module AssociationCollection
|
68
|
+
def self.included(base)
|
69
|
+
base.alias_method_chain :find_target, :inverse_of
|
70
|
+
base.alias_method_chain :add_record_to_target_with_callbacks, :inverse_of
|
71
|
+
end
|
72
|
+
|
73
|
+
def find_target_with_inverse_of
|
74
|
+
records = find_target_without_inverse_of
|
75
|
+
records.each do |record|
|
76
|
+
set_inverse_instance(record, @owner)
|
77
|
+
end
|
78
|
+
records
|
79
|
+
end
|
80
|
+
|
81
|
+
def add_record_to_target_with_callbacks_with_inverse_of(record, &block)
|
82
|
+
record = add_record_to_target_with_callbacks_without_inverse_of(record, &block)
|
83
|
+
set_inverse_instance(record, @owner)
|
84
|
+
record
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
module AssociationProxy
|
89
|
+
def self.included(base)
|
90
|
+
base.alias_method_chain :initialize, :inverse_of
|
91
|
+
end
|
92
|
+
|
93
|
+
def initialize_with_inverse_of(owner, reflection)
|
94
|
+
reflection.check_validity!
|
95
|
+
initialize_without_inverse_of(owner, reflection)
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def set_inverse_instance(record, instance)
|
101
|
+
return if record.nil? || !we_can_set_the_inverse_on_this?(record)
|
102
|
+
inverse_relationship = @reflection.inverse_of
|
103
|
+
unless inverse_relationship.nil?
|
104
|
+
record.send(:"set_#{inverse_relationship.name}_target", instance)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Override in subclasses
|
109
|
+
def we_can_set_the_inverse_on_this?(record)
|
110
|
+
false
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
module BelongsToAssociation
|
115
|
+
def self.included(base)
|
116
|
+
base.alias_method_chain :replace, :inverse_of
|
117
|
+
base.alias_method_chain :find_target, :inverse_of
|
118
|
+
end
|
119
|
+
|
120
|
+
def replace_with_inverse_of(record)
|
121
|
+
replace_without_inverse_of(record)
|
122
|
+
set_inverse_instance(record, @owner)
|
123
|
+
record
|
124
|
+
end
|
125
|
+
|
126
|
+
def find_target_with_inverse_of
|
127
|
+
target = find_target_without_inverse_of and
|
128
|
+
set_inverse_instance(target, @owner)
|
129
|
+
target
|
130
|
+
end
|
131
|
+
|
132
|
+
# NOTE - for now, we're only supporting inverse setting from belongs_to back onto
|
133
|
+
# has_one associations.
|
134
|
+
def we_can_set_the_inverse_on_this?(record)
|
135
|
+
@reflection.has_inverse? && @reflection.inverse_of.macro == :has_one
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
module BelongsToPolymorphicAssociation
|
140
|
+
def self.included(base)
|
141
|
+
base.alias_method_chain :replace, :inverse_of
|
142
|
+
base.alias_method_chain :find_target, :inverse_of
|
143
|
+
end
|
144
|
+
|
145
|
+
def replace_with_inverse_of(record)
|
146
|
+
replace_without_inverse_of(record)
|
147
|
+
set_inverse_instance(record, @owner)
|
148
|
+
record
|
149
|
+
end
|
150
|
+
|
151
|
+
def find_target_with_inverse_of
|
152
|
+
target = find_target_without_inverse_of
|
153
|
+
set_inverse_instance(target, @owner)
|
154
|
+
target
|
155
|
+
end
|
156
|
+
|
157
|
+
# NOTE - for now, we're only supporting inverse setting from belongs_to back onto
|
158
|
+
# has_one associations.
|
159
|
+
def we_can_set_the_inverse_on_this?(record)
|
160
|
+
if @reflection.has_inverse?
|
161
|
+
inverse_association = @reflection.polymorphic_inverse_of(record.class)
|
162
|
+
inverse_association && inverse_association.macro == :has_one
|
163
|
+
else
|
164
|
+
false
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def set_inverse_instance(record, instance)
|
169
|
+
return if record.nil? || !we_can_set_the_inverse_on_this?(record)
|
170
|
+
inverse_relationship = @reflection.polymorphic_inverse_of(record.class)
|
171
|
+
unless inverse_relationship.nil?
|
172
|
+
record.send(:"set_#{inverse_relationship.name}_target", instance)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
module HasManyAssociation
|
178
|
+
def we_can_set_the_inverse_on_this?(record)
|
179
|
+
inverse = @reflection.inverse_of
|
180
|
+
return !inverse.nil?
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
module HasManyThroughAssociation
|
185
|
+
def initialize(owner, reflection)
|
186
|
+
super
|
187
|
+
end
|
188
|
+
|
189
|
+
# NOTE - not sure that we can actually cope with inverses here
|
190
|
+
def we_can_set_the_inverse_on_this?(record)
|
191
|
+
false
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
module HasOneAssociation
|
196
|
+
def self.included(base)
|
197
|
+
base.alias_method_chain :find_target, :inverse_of
|
198
|
+
base.alias_method_chain :new_record, :inverse_of
|
199
|
+
base.alias_method_chain :replace, :inverse_of
|
200
|
+
end
|
201
|
+
|
202
|
+
def find_target_with_inverse_of
|
203
|
+
target = find_target_without_inverse_of
|
204
|
+
set_inverse_instance(target, @owner)
|
205
|
+
target
|
206
|
+
end
|
207
|
+
|
208
|
+
def replace_with_inverse_of(record, dont_save = false)
|
209
|
+
value = replace_without_inverse_of(record, dont_save)
|
210
|
+
set_inverse_instance(record, @owner)
|
211
|
+
value
|
212
|
+
end
|
213
|
+
|
214
|
+
private
|
215
|
+
|
216
|
+
def new_record_with_inverse_of(replace_existing, &block)
|
217
|
+
record = new_record_without_inverse_of(replace_existing, &block)
|
218
|
+
set_inverse_instance(record, @owner) unless replace_existing
|
219
|
+
record
|
220
|
+
end
|
221
|
+
|
222
|
+
def we_can_set_the_inverse_on_this?(record)
|
223
|
+
inverse = @reflection.inverse_of
|
224
|
+
return !inverse.nil?
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
module ClassMethods
|
229
|
+
module JoinDependency
|
230
|
+
def self.included(base)
|
231
|
+
base.alias_method_chain :construct_association, :inverse_of
|
232
|
+
end
|
233
|
+
|
234
|
+
def construct_association_with_inverse_of(record, join, row)
|
235
|
+
association = construct_association_without_inverse_of(record, join, row) or
|
236
|
+
return nil
|
237
|
+
association_proxy = record.send(join.reflection.name)
|
238
|
+
association_proxy.__send__(:set_inverse_instance, association, record)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
module AssociationPreload
|
245
|
+
def self.included(base)
|
246
|
+
base.extend ClassMethods
|
247
|
+
base.metaclass.alias_method_chain :add_preloaded_records_to_collection, :inverse_of
|
248
|
+
base.metaclass.alias_method_chain :set_association_single_records, :inverse_of
|
249
|
+
end
|
250
|
+
|
251
|
+
module ClassMethods
|
252
|
+
def add_preloaded_records_to_collection_with_inverse_of(parent_records, reflection_name, associated_record)
|
253
|
+
value = add_preloaded_records_to_collection_without_inverse_of(parent_records, reflection_name, associated_record)
|
254
|
+
parent_records.each do |parent_record|
|
255
|
+
association_proxy = parent_record.send(reflection_name)
|
256
|
+
association_proxy.__send__(:set_inverse_instance, associated_record, parent_record)
|
257
|
+
end
|
258
|
+
value
|
259
|
+
end
|
260
|
+
|
261
|
+
def set_association_single_records_with_inverse_of(id_to_record_map, reflection_name, associated_records, key)
|
262
|
+
value = set_association_single_records_without_inverse_of(id_to_record_map, reflection_name, associated_records, key)
|
263
|
+
associated_records.each do |associated_record|
|
264
|
+
mapped_records = id_to_record_map[associated_record[key].to_s]
|
265
|
+
mapped_records.each do |mapped_record|
|
266
|
+
association_proxy = mapped_record.send(reflection_name)
|
267
|
+
association_proxy.__send__(:set_inverse_instance, associated_record, mapped_record)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
value
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
ActiveRecord::InverseOfAssociationNotFoundError = InverseOf::InverseOfAssociationNotFoundError
|
277
|
+
ActiveRecord::Associations::AssociationCollection.send :include, InverseOf::Associations::AssociationCollection
|
278
|
+
ActiveRecord::Associations::AssociationProxy.send :include, InverseOf::Associations::AssociationProxy
|
279
|
+
ActiveRecord::Associations::BelongsToAssociation.send :include, InverseOf::Associations::BelongsToAssociation
|
280
|
+
ActiveRecord::Associations::BelongsToPolymorphicAssociation.send :include, InverseOf::Associations::BelongsToPolymorphicAssociation
|
281
|
+
ActiveRecord::Associations::HasManyAssociation.send :include, InverseOf::Associations::HasManyAssociation
|
282
|
+
ActiveRecord::Associations::HasManyThroughAssociation.send :include, InverseOf::Associations::HasManyThroughAssociation
|
283
|
+
ActiveRecord::Associations::HasOneAssociation.send :include, InverseOf::Associations::HasOneAssociation
|
284
|
+
ActiveRecord::Associations::ClassMethods::JoinDependency.send :include, InverseOf::Associations::ClassMethods::JoinDependency
|
285
|
+
ActiveRecord::Reflection.send :include, InverseOf::Reflection
|
286
|
+
ActiveRecord::Base.send :include, InverseOf::AssociationPreload
|
287
|
+
|
288
|
+
module ActiveRecord::Associations::ClassMethods
|
289
|
+
@@valid_keys_for_has_many_association << :inverse_of
|
290
|
+
@@valid_keys_for_has_one_association << :inverse_of
|
291
|
+
@@valid_keys_for_belongs_to_association << :inverse_of
|
292
|
+
end
|
293
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'inverse_of'
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'ruby-debug'
|
3
|
+
require 'active_record'
|
4
|
+
require 'active_record/test_case'
|
5
|
+
require 'active_record/fixtures'
|
6
|
+
|
7
|
+
ROOT = File.dirname(File.dirname(File.dirname(__FILE__)))
|
8
|
+
|
9
|
+
require "#{ROOT}/rails/init"
|
10
|
+
|
11
|
+
class ActiveSupport::TestCase
|
12
|
+
include ActiveRecord::TestFixtures
|
13
|
+
|
14
|
+
self.fixture_path = "#{ROOT}/test/fixtures"
|
15
|
+
self.use_instantiated_fixtures = false
|
16
|
+
self.use_transactional_fixtures = true
|
17
|
+
|
18
|
+
def create_fixtures(*table_names, &block)
|
19
|
+
Fixtures.create_fixtures(ActiveSupport::TestCase.fixture_path, table_names, {}, &block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
ActiveRecord::Base.configurations = {'test' => {'adapter' => "sqlite3", 'database' => ":memory:"}}
|
24
|
+
ActiveRecord::Base.establish_connection('test')
|
25
|
+
ActiveRecord::Base.connection.instance_eval do
|
26
|
+
eval File.read("#{ROOT}/test/schema/schema.rb")
|
27
|
+
end
|
28
|
+
Dir["#{ROOT}/test/models/*.rb"].each{|path| require path}
|