polyamorous 1.1.0 → 1.2.0
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.
- checksums.yaml +4 -4
- data/.travis.yml +27 -6
- data/Gemfile +15 -13
- data/README.md +7 -2
- data/lib/polyamorous.rb +30 -12
- data/lib/polyamorous/{activerecord_3_and_4.0 → activerecord_3_and_4.0_ruby_1.9}/join_association.rb +28 -27
- data/lib/polyamorous/{activerecord_3_and_4.0 → activerecord_3_and_4.0_ruby_1.9}/join_dependency.rb +7 -4
- data/lib/polyamorous/activerecord_4.1_ruby_1.9/join_association.rb +2 -0
- data/lib/polyamorous/activerecord_4.1_ruby_1.9/join_dependency.rb +4 -0
- data/lib/polyamorous/activerecord_4.1_ruby_2/join_association.rb +2 -0
- data/lib/polyamorous/activerecord_4.1_ruby_2/join_dependency.rb +3 -0
- data/lib/polyamorous/activerecord_4.1_ruby_2/make_joins.rb +11 -0
- data/lib/polyamorous/activerecord_4.2_ruby_1.9/join_association.rb +46 -0
- data/lib/polyamorous/activerecord_4.2_ruby_1.9/join_dependency.rb +94 -0
- data/lib/polyamorous/activerecord_4.2_ruby_2/join_association.rb +37 -0
- data/lib/polyamorous/{activerecord_4.1 → activerecord_4.2_ruby_2}/join_dependency.rb +39 -44
- data/lib/polyamorous/swapping_reflection_class.rb +11 -0
- data/lib/polyamorous/version.rb +1 -1
- data/polyamorous.gemspec +4 -3
- data/spec/polyamorous/join_association_spec.rb +3 -3
- data/spec/polyamorous/join_dependency_spec.rb +3 -3
- data/spec/polyamorous/join_spec.rb +2 -2
- data/spec/spec_helper.rb +10 -2
- data/spec/support/schema.rb +47 -51
- data/spec/support/shared_examples/join_association_3_and_4.0.rb +6 -3
- data/spec/support/shared_examples/join_association_4.1.rb +9 -4
- data/spec/support/shared_examples/join_dependency_3_and_4.0.rb +22 -11
- data/spec/support/shared_examples/join_dependency_4.1.rb +25 -6
- metadata +41 -12
- data/lib/polyamorous/activerecord_4.1/join_association.rb +0 -75
- data/lib/polyamorous/activerecord_4.2/join_dependency.rb +0 -12
@@ -0,0 +1,37 @@
|
|
1
|
+
# active_record_4.2_ruby_2/join_association.rb
|
2
|
+
module Polyamorous
|
3
|
+
module JoinAssociationExtensions
|
4
|
+
include SwappingReflectionClass
|
5
|
+
def self.prepended(base)
|
6
|
+
base.class_eval { attr_reader :join_type }
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(reflection, children, polymorphic_class = nil,
|
10
|
+
join_type = Arel::Nodes::InnerJoin)
|
11
|
+
@join_type = join_type
|
12
|
+
if polymorphic_class && ::ActiveRecord::Base > polymorphic_class
|
13
|
+
swapping_reflection_klass(reflection, polymorphic_class) do |reflection|
|
14
|
+
super(reflection, children)
|
15
|
+
self.reflection.options[:polymorphic] = true
|
16
|
+
end
|
17
|
+
else
|
18
|
+
super(reflection, children)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Reference https://github.com/rails/rails/commit/9b15db51b78028bfecdb85595624de4b838adbd1
|
23
|
+
# NOTE Not sure we still need it?
|
24
|
+
def ==(other)
|
25
|
+
base_klass == other.base_klass
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_constraint(klass, table, key, foreign_table, foreign_key)
|
29
|
+
if reflection.polymorphic?
|
30
|
+
super(klass, table, key, foreign_table, foreign_key)
|
31
|
+
.and(foreign_table[reflection.foreign_type].eq(reflection.klass.name))
|
32
|
+
else
|
33
|
+
super(klass, table, key, foreign_table, foreign_key)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -1,41 +1,33 @@
|
|
1
|
+
# active_record_4.2_ruby_2/join_dependency.rb
|
1
2
|
module Polyamorous
|
2
3
|
module JoinDependencyExtensions
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
base.class_eval do
|
7
|
-
class << self
|
8
|
-
alias_method_chain :walk_tree, :polymorphism
|
9
|
-
end
|
10
|
-
|
11
|
-
alias_method_chain :build, :polymorphism
|
12
|
-
alias_method_chain :join_constraints, :polymorphism
|
13
|
-
|
14
|
-
if base.method_defined?(:active_record)
|
15
|
-
alias_method :base_klass, :active_record
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def build_with_polymorphism(associations, base_klass)
|
4
|
+
# Replaces ActiveRecord::Associations::JoinDependency#build
|
5
|
+
def build(associations, base_klass)
|
21
6
|
associations.map do |name, right|
|
22
7
|
if name.is_a? Join
|
23
8
|
reflection = find_reflection base_klass, name.name
|
24
9
|
reflection.check_validity!
|
25
|
-
|
26
|
-
|
27
|
-
|
10
|
+
if reflection.polymorphic?
|
11
|
+
JoinAssociation.new(
|
12
|
+
reflection,
|
13
|
+
build(right, name.klass || base_klass),
|
14
|
+
name.klass,
|
15
|
+
name.type
|
16
|
+
)
|
28
17
|
else
|
29
|
-
JoinAssociation.new
|
18
|
+
JoinAssociation.new(
|
19
|
+
reflection,
|
20
|
+
build(right, reflection.klass),
|
21
|
+
name.klass,
|
22
|
+
name.type
|
23
|
+
)
|
30
24
|
end
|
31
25
|
else
|
32
26
|
reflection = find_reflection base_klass, name
|
33
27
|
reflection.check_validity!
|
34
|
-
|
35
|
-
if reflection.options[:polymorphic]
|
28
|
+
if reflection.polymorphic?
|
36
29
|
raise ActiveRecord::EagerLoadPolymorphicError.new(reflection)
|
37
30
|
end
|
38
|
-
|
39
31
|
JoinAssociation.new reflection, build(right, reflection.klass)
|
40
32
|
end
|
41
33
|
end
|
@@ -43,7 +35,7 @@ module Polyamorous
|
|
43
35
|
|
44
36
|
def find_join_association_respecting_polymorphism(reflection, parent, klass)
|
45
37
|
if association = parent.children.find { |j| j.reflection == reflection }
|
46
|
-
unless reflection.
|
38
|
+
unless reflection.polymorphic?
|
47
39
|
association
|
48
40
|
else
|
49
41
|
association if association.base_klass == klass
|
@@ -52,40 +44,44 @@ module Polyamorous
|
|
52
44
|
end
|
53
45
|
|
54
46
|
def build_join_association_respecting_polymorphism(reflection, parent, klass)
|
55
|
-
if reflection.
|
47
|
+
if reflection.polymorphic? && klass
|
56
48
|
JoinAssociation.new(reflection, self, klass)
|
57
49
|
else
|
58
50
|
JoinAssociation.new(reflection, self)
|
59
51
|
end
|
60
52
|
end
|
61
53
|
|
62
|
-
|
54
|
+
# Replaces ActiveRecord::Associations::JoinDependency#join_constraints
|
55
|
+
# to call #make_joins instead of #make_inner_joins.
|
56
|
+
def join_constraints(outer_joins)
|
63
57
|
joins = join_root.children.flat_map { |child|
|
64
|
-
make_joins
|
58
|
+
make_joins(join_root, child)
|
65
59
|
}
|
66
|
-
|
67
60
|
joins.concat outer_joins.flat_map { |oj|
|
68
61
|
if join_root.match? oj.join_root
|
69
|
-
walk
|
62
|
+
walk(join_root, oj.join_root)
|
70
63
|
else
|
71
64
|
oj.join_root.children.flat_map { |child|
|
72
|
-
make_outer_joins
|
65
|
+
make_outer_joins(oj.join_root, child)
|
73
66
|
}
|
74
67
|
end
|
75
68
|
}
|
76
69
|
end
|
77
70
|
|
71
|
+
# Replaces ActiveRecord::Associations::JoinDependency#make_inner_joins
|
78
72
|
def make_joins(parent, child)
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
73
|
+
[
|
74
|
+
make_constraints(
|
75
|
+
parent, child, child.tables, child.join_type || Arel::Nodes::InnerJoin
|
76
|
+
)
|
77
|
+
] + child.children.flat_map { |c| make_inner_joins(child, c) }
|
83
78
|
end
|
84
79
|
|
85
80
|
private :make_joins
|
86
81
|
|
87
82
|
module ClassMethods
|
88
|
-
|
83
|
+
# Prepended before ActiveRecord::Associations::JoinDependency#self.walk_tree
|
84
|
+
def walk_tree(associations, hash)
|
89
85
|
case associations
|
90
86
|
when TreeNode
|
91
87
|
associations.add_to_tree(hash)
|
@@ -93,16 +89,15 @@ module Polyamorous
|
|
93
89
|
associations.each do |k, v|
|
94
90
|
cache =
|
95
91
|
case k
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
92
|
+
when TreeNode
|
93
|
+
k.add_to_tree(hash)
|
94
|
+
else
|
95
|
+
hash[k] ||= {}
|
96
|
+
end
|
102
97
|
walk_tree(v, cache)
|
103
98
|
end
|
104
99
|
else
|
105
|
-
|
100
|
+
super(associations, hash)
|
106
101
|
end
|
107
102
|
end
|
108
103
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Polyamorous
|
2
|
+
module SwappingReflectionClass
|
3
|
+
def swapping_reflection_klass(reflection, klass)
|
4
|
+
new_reflection = reflection.clone
|
5
|
+
new_reflection.instance_variable_set(:@options, reflection.options.clone)
|
6
|
+
new_reflection.options.delete(:polymorphic)
|
7
|
+
new_reflection.instance_variable_set(:@klass, klass)
|
8
|
+
yield new_reflection
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/lib/polyamorous/version.rb
CHANGED
data/polyamorous.gemspec
CHANGED
@@ -5,9 +5,10 @@ require "polyamorous/version"
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "polyamorous"
|
7
7
|
s.version = Polyamorous::VERSION
|
8
|
-
s.authors = ["Ernie Miller"]
|
9
|
-
s.email = ["ernie@
|
10
|
-
s.homepage = "
|
8
|
+
s.authors = ["Ernie Miller", "Ryan Bigg", "Jon Atack", "Xiang Li"]
|
9
|
+
s.email = ["ernie@erniemiller.org", "radarlistener@gmail.com", "jonnyatack@gmail.com", "bigxiang@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/activerecord-hackery/polyamorous"
|
11
|
+
s.license = "MIT"
|
11
12
|
s.summary = %q{
|
12
13
|
Loves/is loved by polymorphic belongs_to associations, Ransack, Squeel, MetaSearch...
|
13
14
|
}
|
@@ -2,10 +2,10 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module Polyamorous
|
4
4
|
describe JoinAssociation do
|
5
|
-
if ActiveRecord::VERSION::STRING >=
|
6
|
-
include_examples
|
5
|
+
if ActiveRecord::VERSION::STRING >= '4.1'
|
6
|
+
include_examples 'Join Association on ActiveRecord 4.1'
|
7
7
|
else
|
8
|
-
include_examples
|
8
|
+
include_examples 'Join Association on ActiveRecord 3 and 4.0'
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
@@ -2,10 +2,10 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module Polyamorous
|
4
4
|
describe JoinDependency do
|
5
|
-
if ActiveRecord::VERSION::STRING >=
|
6
|
-
include_examples
|
5
|
+
if ActiveRecord::VERSION::STRING >= '4.1'
|
6
|
+
include_examples 'Join Dependency on ActiveRecord 4.1'
|
7
7
|
else
|
8
|
-
include_examples
|
8
|
+
include_examples 'Join Dependency on ActiveRecord 3 and 4.0'
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
@@ -2,12 +2,12 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module Polyamorous
|
4
4
|
describe Join do
|
5
|
-
it
|
5
|
+
it 'is a tree node' do
|
6
6
|
join = new_join(:articles, :outer)
|
7
7
|
expect(join).to be_kind_of(TreeNode)
|
8
8
|
end
|
9
9
|
|
10
|
-
it
|
10
|
+
it 'can be add to a tree' do
|
11
11
|
join = new_join(:articles, :outer)
|
12
12
|
|
13
13
|
tree_hash = {}
|
data/spec/spec_helper.rb
CHANGED
@@ -20,7 +20,15 @@ Sham.define do
|
|
20
20
|
end
|
21
21
|
|
22
22
|
RSpec.configure do |config|
|
23
|
-
config.before(:suite)
|
23
|
+
config.before(:suite) do
|
24
|
+
message = "Running Polyamorous specs with #{
|
25
|
+
ActiveRecord::Base.connection.adapter_name
|
26
|
+
}, Active Record #{::ActiveRecord::VERSION::STRING}, Arel #{Arel::VERSION
|
27
|
+
} and Ruby #{RUBY_VERSION}"
|
28
|
+
line = '=' * message.length
|
29
|
+
puts line, message, line
|
30
|
+
Schema.create
|
31
|
+
end
|
24
32
|
config.before(:all) { Sham.reset(:before_all) }
|
25
33
|
config.before(:each) { Sham.reset(:before_each) }
|
26
34
|
|
@@ -32,4 +40,4 @@ RSpec::Matchers.define :be_like do |expected|
|
|
32
40
|
actual.gsub(/^\s+|\s+$/, '').gsub(/\s+/, ' ').strip ==
|
33
41
|
expected.gsub(/^\s+|\s+$/, '').gsub(/\s+/, ' ').strip
|
34
42
|
end
|
35
|
-
end
|
43
|
+
end
|
data/spec/support/schema.rb
CHANGED
@@ -1,25 +1,22 @@
|
|
1
1
|
require 'active_record'
|
2
2
|
|
3
|
-
ActiveRecord::Base.establish_connection(
|
4
|
-
:adapter => 'sqlite3',
|
5
|
-
:database => ':memory:'
|
6
|
-
)
|
3
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
7
4
|
|
8
5
|
class Person < ActiveRecord::Base
|
9
|
-
belongs_to :parent, :
|
10
|
-
has_many :children, :
|
6
|
+
belongs_to :parent, class_name: 'Person', foreign_key: :parent_id
|
7
|
+
has_many :children, class_name: 'Person', foreign_key: :parent_id
|
11
8
|
has_many :articles
|
12
9
|
has_many :comments
|
13
|
-
has_many :authored_article_comments, :
|
14
|
-
:
|
15
|
-
has_many :notes, :
|
10
|
+
has_many :authored_article_comments, through: :articles,
|
11
|
+
foreign_key: :person_id, source: :comments
|
12
|
+
has_many :notes, as: :notable
|
16
13
|
end
|
17
14
|
|
18
15
|
class Article < ActiveRecord::Base
|
19
16
|
belongs_to :person
|
20
17
|
has_many :comments
|
21
18
|
has_and_belongs_to_many :tags
|
22
|
-
has_many :notes,
|
19
|
+
has_many :notes, as: :notable
|
23
20
|
end
|
24
21
|
|
25
22
|
class Comment < ActiveRecord::Base
|
@@ -32,71 +29,70 @@ class Tag < ActiveRecord::Base
|
|
32
29
|
end
|
33
30
|
|
34
31
|
class Note < ActiveRecord::Base
|
35
|
-
belongs_to :notable, :
|
32
|
+
belongs_to :notable, polymorphic: true
|
36
33
|
end
|
37
34
|
|
38
35
|
module Schema
|
39
36
|
def self.create
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
create_table :articles, :force => true do |t|
|
52
|
-
t.integer :person_id
|
53
|
-
t.string :title
|
54
|
-
t.text :body
|
55
|
-
end
|
37
|
+
ActiveRecord::Migration.verbose = false
|
38
|
+
|
39
|
+
ActiveRecord::Schema.define do
|
40
|
+
create_table :people, force: true do |t|
|
41
|
+
t.integer :parent_id
|
42
|
+
t.string :name
|
43
|
+
t.integer :salary
|
44
|
+
t.boolean :awesome, default: false
|
45
|
+
t.timestamps null: false
|
46
|
+
end
|
56
47
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
48
|
+
create_table :articles, force: true do |t|
|
49
|
+
t.integer :person_id
|
50
|
+
t.string :title
|
51
|
+
t.text :body
|
52
|
+
end
|
62
53
|
|
63
|
-
|
64
|
-
|
65
|
-
|
54
|
+
create_table :comments, force: true do |t|
|
55
|
+
t.integer :article_id
|
56
|
+
t.integer :person_id
|
57
|
+
t.text :body
|
58
|
+
end
|
66
59
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end
|
60
|
+
create_table :tags, force: true do |t|
|
61
|
+
t.string :name
|
62
|
+
end
|
71
63
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
end
|
64
|
+
create_table :articles_tags, force: true, id: false do |t|
|
65
|
+
t.integer :article_id
|
66
|
+
t.integer :tag_id
|
67
|
+
end
|
77
68
|
|
69
|
+
create_table :notes, force: true do |t|
|
70
|
+
t.integer :notable_id
|
71
|
+
t.string :notable_type
|
72
|
+
t.string :note
|
78
73
|
end
|
79
74
|
end
|
80
75
|
|
81
76
|
10.times do
|
82
77
|
person = Person.make
|
83
|
-
Note.make(:
|
78
|
+
Note.make(notable: person)
|
84
79
|
3.times do
|
85
|
-
article = Article.make(:
|
80
|
+
article = Article.make(person: person)
|
86
81
|
3.times do
|
87
82
|
article.tags = [Tag.make, Tag.make, Tag.make]
|
88
83
|
end
|
89
|
-
Note.make(:
|
84
|
+
Note.make(notable: article)
|
90
85
|
10.times do
|
91
|
-
Comment.make(:
|
86
|
+
Comment.make(article: article)
|
92
87
|
end
|
93
88
|
end
|
94
89
|
2.times do
|
95
|
-
Comment.make(:
|
90
|
+
Comment.make(person: person)
|
96
91
|
end
|
97
92
|
end
|
98
93
|
|
99
|
-
Comment.make(
|
100
|
-
|
94
|
+
Comment.make(
|
95
|
+
body: 'First post!', article: Article.make(title: 'Hello, world!')
|
96
|
+
)
|
101
97
|
end
|
102
98
|
end
|
@@ -1,8 +1,10 @@
|
|
1
|
-
shared_examples
|
1
|
+
shared_examples 'Join Association on ActiveRecord 3 and 4.0' do
|
2
2
|
let(:join_dependency) { new_join_dependency Note, {} }
|
3
3
|
let(:reflection) { Note.reflect_on_association(:notable) }
|
4
4
|
let(:parent) { join_dependency.join_base }
|
5
|
-
let(:join_association) {
|
5
|
+
let(:join_association) {
|
6
|
+
new_join_association(reflection, join_dependency, parent, Article)
|
7
|
+
}
|
6
8
|
|
7
9
|
subject {
|
8
10
|
join_dependency.build_join_association_respecting_polymorphism(
|
@@ -25,7 +27,8 @@ shared_examples "Join Association on ActiveRecord 3 and 4.0" do
|
|
25
27
|
|
26
28
|
it 'leaves the orginal reflection intact for thread safety' do
|
27
29
|
reflection.instance_variable_set(:@klass, Article)
|
28
|
-
join_association
|
30
|
+
join_association
|
31
|
+
.swapping_reflection_klass(reflection, Person) do |new_reflection|
|
29
32
|
expect(new_reflection.options).not_to equal reflection.options
|
30
33
|
expect(new_reflection.options).not_to have_key(:polymorphic)
|
31
34
|
expect(new_reflection.klass).to eq(Person)
|