includes_many 0.1.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 +7 -0
- data/.gitignore +24 -0
- data/.rspec +2 -0
- data/.travis.yml +14 -0
- data/Appraisals +18 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +22 -0
- data/README.md +66 -0
- data/Rakefile +18 -0
- data/certs/razum2um.pem +21 -0
- data/gemfiles/.gitignore +1 -0
- data/gemfiles/rails_3_2.gemfile +14 -0
- data/gemfiles/rails_4_0.gemfile +14 -0
- data/gemfiles/rails_4_1.gemfile +14 -0
- data/gemfiles/rails_4_2.gemfile +17 -0
- data/includes_many.gemspec +30 -0
- data/lib/includes_many.rb +19 -0
- data/lib/includes_many/active_record32.rb +4 -0
- data/lib/includes_many/active_record32/association_scope.rb +93 -0
- data/lib/includes_many/active_record32/has_many.rb +43 -0
- data/lib/includes_many/active_record32/join_association.rb +37 -0
- data/lib/includes_many/active_record40.rb +4 -0
- data/lib/includes_many/active_record40/association_scope.rb +97 -0
- data/lib/includes_many/active_record40/has_many.rb +32 -0
- data/lib/includes_many/active_record40/join_association.rb +35 -0
- data/lib/includes_many/active_record41.rb +4 -0
- data/lib/includes_many/active_record41/association_scope.rb +88 -0
- data/lib/includes_many/active_record41/has_many.rb +34 -0
- data/lib/includes_many/active_record41/join_association.rb +30 -0
- data/lib/includes_many/active_record42.rb +4 -0
- data/lib/includes_many/active_record42/association_scope.rb +81 -0
- data/lib/includes_many/active_record42/has_many.rb +34 -0
- data/lib/includes_many/active_record42/join_association.rb +30 -0
- data/lib/includes_many/version.rb +3 -0
- data/spec/includes_many_spec.rb +83 -0
- data/spec/internal/app/models/comment.rb +12 -0
- data/spec/internal/config/database.yml +9 -0
- data/spec/internal/db/schema.rb +6 -0
- data/spec/internal/log/.gitkeep +0 -0
- data/spec/spec_helper.rb +26 -0
- metadata +207 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class NonScalarPrimaryKeyError < ::ActiveRecord::ActiveRecordError
|
3
|
+
def initialize(reflection)
|
4
|
+
super("Can not join association #{reflection.name.inspect}, because :primary_key is a callable. Use Relation#includes or specify a join column name")
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module Associations
|
9
|
+
class JoinDependency
|
10
|
+
class JoinAssociation
|
11
|
+
def initialize(reflection, join_dependency, parent = nil)
|
12
|
+
reflection.check_validity!
|
13
|
+
|
14
|
+
# PATCH here
|
15
|
+
if reflection.options[:primary_key].respond_to?(:call)
|
16
|
+
raise NonScalarPrimaryKeyError.new(reflection)
|
17
|
+
end
|
18
|
+
# end PATCH
|
19
|
+
|
20
|
+
if reflection.options[:polymorphic]
|
21
|
+
raise EagerLoadPolymorphicError.new(reflection)
|
22
|
+
end
|
23
|
+
|
24
|
+
super(reflection.klass)
|
25
|
+
|
26
|
+
@reflection = reflection
|
27
|
+
@join_dependency = join_dependency
|
28
|
+
@parent = parent
|
29
|
+
@join_type = Arel::InnerJoin
|
30
|
+
@aliased_prefix = "t#{ join_dependency.join_parts.size }"
|
31
|
+
@tables = construct_tables.reverse
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class AssociationScope
|
4
|
+
safe_monkeypatch :add_constraints, md5: 'fa15ef6f1cd4fb164746dc2bb3dcb887'
|
5
|
+
|
6
|
+
def add_constraints(scope)
|
7
|
+
tables = construct_tables
|
8
|
+
|
9
|
+
chain.each_with_index do |reflection, i|
|
10
|
+
table, foreign_table = tables.shift, tables.first
|
11
|
+
|
12
|
+
if reflection.source_macro == :has_and_belongs_to_many
|
13
|
+
join_table = tables.shift
|
14
|
+
|
15
|
+
scope = scope.joins(join(
|
16
|
+
join_table,
|
17
|
+
table[reflection.association_primary_key].
|
18
|
+
eq(join_table[reflection.association_foreign_key])
|
19
|
+
))
|
20
|
+
|
21
|
+
table, foreign_table = join_table, tables.first
|
22
|
+
end
|
23
|
+
|
24
|
+
if reflection.source_macro == :belongs_to
|
25
|
+
if reflection.options[:polymorphic]
|
26
|
+
key = reflection.association_primary_key(self.klass)
|
27
|
+
else
|
28
|
+
key = reflection.association_primary_key
|
29
|
+
end
|
30
|
+
|
31
|
+
foreign_key = reflection.foreign_key
|
32
|
+
else
|
33
|
+
key = reflection.foreign_key
|
34
|
+
foreign_key = reflection.active_record_primary_key
|
35
|
+
end
|
36
|
+
|
37
|
+
if reflection == chain.last
|
38
|
+
bind_arel = if foreign_key.respond_to?(:call)
|
39
|
+
fk = foreign_key.call(owner)
|
40
|
+
if fk.respond_to?(:each)
|
41
|
+
bind_vals = fk.compact.map do |f|
|
42
|
+
bind scope, table.table_name, key.to_s, f
|
43
|
+
end
|
44
|
+
table[key].in(bind_vals)
|
45
|
+
else
|
46
|
+
bind_val = bind scope, table.table_name, key.to_s, fk
|
47
|
+
table[key].eq(bind_val)
|
48
|
+
end
|
49
|
+
else
|
50
|
+
bind_val = bind scope, table.table_name, reflection.type.to_s, value
|
51
|
+
table[reflection.type].eq(bind_val)
|
52
|
+
end
|
53
|
+
scope = scope.where(bind_arel)
|
54
|
+
|
55
|
+
if reflection.type
|
56
|
+
value = owner.class.base_class.name
|
57
|
+
bind_val = bind scope, table.table_name, reflection.type.to_s, value
|
58
|
+
scope = scope.where(table[reflection.type].eq(bind_val))
|
59
|
+
end
|
60
|
+
else
|
61
|
+
constraint = table[key].eq(foreign_table[foreign_key])
|
62
|
+
|
63
|
+
if reflection.type
|
64
|
+
type = chain[i + 1].klass.base_class.name
|
65
|
+
constraint = constraint.and(table[reflection.type].eq(type))
|
66
|
+
end
|
67
|
+
|
68
|
+
scope = scope.joins(join(foreign_table, constraint))
|
69
|
+
end
|
70
|
+
|
71
|
+
is_first_chain = i == 0
|
72
|
+
klass = is_first_chain ? self.klass : reflection.klass
|
73
|
+
|
74
|
+
# Exclude the scope of the association itself, because that
|
75
|
+
# was already merged in the #scope method.
|
76
|
+
scope_chain[i].each do |scope_chain_item|
|
77
|
+
item = eval_scope(klass, scope_chain_item)
|
78
|
+
|
79
|
+
if scope_chain_item == self.reflection.scope
|
80
|
+
scope.merge! item.except(:where, :includes)
|
81
|
+
end
|
82
|
+
|
83
|
+
if is_first_chain
|
84
|
+
scope.includes! item.includes_values
|
85
|
+
end
|
86
|
+
|
87
|
+
scope.where_values += item.where_values
|
88
|
+
scope.order_values |= item.order_values
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
scope
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class Preloader
|
4
|
+
class HasMany
|
5
|
+
safe_monkeypatch :owners_by_key, md5: '33eab3037fd994d4a7106a68a05f169d'
|
6
|
+
|
7
|
+
def owners_by_key
|
8
|
+
@owners_by_key ||= begin
|
9
|
+
res = Hash.new { |h,k| h[k] = Set.new }
|
10
|
+
owners.each do |owner|
|
11
|
+
key = if owner_key_name.respond_to?(:call)
|
12
|
+
owner_key_name.call(owner)
|
13
|
+
else
|
14
|
+
owner[owner_key_name]
|
15
|
+
end
|
16
|
+
|
17
|
+
if key.respond_to?(:each)
|
18
|
+
key.each do |k|
|
19
|
+
res[k && k.to_s] << owner
|
20
|
+
end
|
21
|
+
else
|
22
|
+
res[key && key.to_s] << owner
|
23
|
+
end
|
24
|
+
end
|
25
|
+
res
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class NonScalarPrimaryKeyError < ActiveRecordError
|
3
|
+
def initialize(reflection)
|
4
|
+
super("Can not join association #{reflection.name.inspect}, because :primary_key is a callable. Use Relation#includes or specify a join column name")
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module Associations
|
9
|
+
class JoinDependency
|
10
|
+
class JoinAssociation
|
11
|
+
safe_monkeypatch :initialize, md5: 'b1b62d42c897ef2e2c4db921a97bad5b'
|
12
|
+
|
13
|
+
def initialize(reflection, join_dependency, parent = nil)
|
14
|
+
reflection.check_validity!
|
15
|
+
|
16
|
+
# PATCH here
|
17
|
+
if reflection.options[:primary_key].respond_to?(:call)
|
18
|
+
raise NonScalarPrimaryKeyError.new(reflection)
|
19
|
+
end
|
20
|
+
# end PATCH
|
21
|
+
|
22
|
+
super(reflection.klass)
|
23
|
+
|
24
|
+
@reflection = reflection
|
25
|
+
@join_dependency = join_dependency
|
26
|
+
@parent = parent
|
27
|
+
@join_type = Arel::InnerJoin
|
28
|
+
@aliased_prefix = "t#{ join_dependency.join_parts.size }"
|
29
|
+
@tables = construct_tables.reverse
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class AssociationScope
|
4
|
+
safe_monkeypatch :add_constraints, md5: '0bfea41607ffb2444b96cb429663fb63'
|
5
|
+
|
6
|
+
def add_constraints(scope, owner, assoc_klass, refl, tracker)
|
7
|
+
chain = refl.chain
|
8
|
+
scope_chain = refl.scope_chain
|
9
|
+
|
10
|
+
tables = construct_tables(chain, assoc_klass, refl, tracker)
|
11
|
+
|
12
|
+
chain.each_with_index do |reflection, i|
|
13
|
+
table, foreign_table = tables.shift, tables.first
|
14
|
+
|
15
|
+
if reflection.source_macro == :belongs_to
|
16
|
+
if reflection.options[:polymorphic]
|
17
|
+
key = reflection.association_primary_key(assoc_klass)
|
18
|
+
else
|
19
|
+
key = reflection.association_primary_key
|
20
|
+
end
|
21
|
+
|
22
|
+
foreign_key = reflection.foreign_key
|
23
|
+
else
|
24
|
+
key = reflection.foreign_key
|
25
|
+
foreign_key = reflection.active_record_primary_key
|
26
|
+
end
|
27
|
+
|
28
|
+
if reflection == chain.last
|
29
|
+
bind_arel = if foreign_key.respond_to?(:call)
|
30
|
+
fk = foreign_key.call(owner)
|
31
|
+
if fk.respond_to?(:each)
|
32
|
+
bind_vals = fk.compact.map do |f|
|
33
|
+
bind scope, table.table_name, key.to_s, f, tracker
|
34
|
+
end
|
35
|
+
table[key].in(bind_vals)
|
36
|
+
else
|
37
|
+
table[key].eq(fk)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key], tracker
|
41
|
+
table[key].eq(bind_val)
|
42
|
+
end
|
43
|
+
scope = scope.where(bind_arel)
|
44
|
+
|
45
|
+
if reflection.type
|
46
|
+
value = owner.class.base_class.name
|
47
|
+
bind_val = bind scope, table.table_name, reflection.type.to_s, value, tracker
|
48
|
+
scope = scope.where(table[reflection.type].eq(bind_val))
|
49
|
+
end
|
50
|
+
else
|
51
|
+
constraint = table[key].eq(foreign_table[foreign_key])
|
52
|
+
|
53
|
+
if reflection.type
|
54
|
+
value = chain[i + 1].klass.base_class.name
|
55
|
+
bind_val = bind scope, table.table_name, reflection.type.to_s, value, tracker
|
56
|
+
scope = scope.where(table[reflection.type].eq(bind_val))
|
57
|
+
end
|
58
|
+
|
59
|
+
scope = scope.joins(join(foreign_table, constraint))
|
60
|
+
end
|
61
|
+
|
62
|
+
is_first_chain = i == 0
|
63
|
+
klass = is_first_chain ? assoc_klass : reflection.klass
|
64
|
+
|
65
|
+
# Exclude the scope of the association itself, because that
|
66
|
+
# was already merged in the #scope method.
|
67
|
+
scope_chain[i].each do |scope_chain_item|
|
68
|
+
item = eval_scope(klass, scope_chain_item, owner)
|
69
|
+
|
70
|
+
if scope_chain_item == refl.scope
|
71
|
+
scope.merge! item.except(:where, :includes, :bind)
|
72
|
+
end
|
73
|
+
|
74
|
+
if is_first_chain
|
75
|
+
scope.includes! item.includes_values
|
76
|
+
end
|
77
|
+
|
78
|
+
scope.where_values += item.where_values
|
79
|
+
scope.order_values |= item.order_values
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
scope
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class Preloader
|
4
|
+
class HasMany
|
5
|
+
safe_monkeypatch :owners_by_key, md5: '70b0628dd3c79928ee0bcff75052894a'
|
6
|
+
|
7
|
+
def owners_by_key
|
8
|
+
@owners_by_key ||= begin
|
9
|
+
res = Hash.new { |h,k| h[k] = Set.new }
|
10
|
+
owners.each do |owner|
|
11
|
+
key = if owner_key_name.respond_to?(:call)
|
12
|
+
owner_key_name.call(owner)
|
13
|
+
else
|
14
|
+
owner[owner_key_name]
|
15
|
+
end
|
16
|
+
|
17
|
+
if key.respond_to?(:each)
|
18
|
+
key.each do |k|
|
19
|
+
k = k.to_s if key_conversion_required?
|
20
|
+
res[k] << owner
|
21
|
+
end
|
22
|
+
else
|
23
|
+
key = key.to_s if key_conversion_required?
|
24
|
+
res[key] << owner
|
25
|
+
end
|
26
|
+
end
|
27
|
+
res
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class NonScalarPrimaryKeyError < ActiveRecordError
|
3
|
+
def initialize(reflection)
|
4
|
+
super("Can not join association #{reflection.name.inspect}, because :primary_key is a callable. Use Relation#includes or specify a join column name")
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module Associations
|
9
|
+
class JoinDependency
|
10
|
+
class JoinAssociation
|
11
|
+
safe_monkeypatch :initialize, md5: '2332c88140aa9e9efa96975fd48e6958'
|
12
|
+
|
13
|
+
def initialize(reflection, children)
|
14
|
+
|
15
|
+
# PATCH here
|
16
|
+
if reflection.options[:primary_key].respond_to?(:call)
|
17
|
+
raise NonScalarPrimaryKeyError.new(reflection)
|
18
|
+
end
|
19
|
+
# end PATCH
|
20
|
+
|
21
|
+
super(reflection.klass, children)
|
22
|
+
|
23
|
+
@reflection = reflection
|
24
|
+
@tables = nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class AssociationScope
|
4
|
+
safe_monkeypatch :add_constraints, md5: '191225b52dbc5ff8fd658b76a9b9b840', error: 'fails on Fri Aug 8 16:10:06 2014 +0200 f4c8ce9'
|
5
|
+
|
6
|
+
def add_constraints(scope, owner, assoc_klass, refl, tracker)
|
7
|
+
chain = refl.chain
|
8
|
+
scope_chain = refl.scope_chain
|
9
|
+
|
10
|
+
tables = construct_tables(chain, assoc_klass, refl, tracker)
|
11
|
+
|
12
|
+
chain.each_with_index do |reflection, i|
|
13
|
+
table, foreign_table = tables.shift, tables.first
|
14
|
+
|
15
|
+
join_keys = reflection.join_keys(assoc_klass)
|
16
|
+
key = join_keys.key
|
17
|
+
foreign_key = join_keys.foreign_key
|
18
|
+
|
19
|
+
if reflection == chain.last
|
20
|
+
bind_arel = if foreign_key.respond_to?(:call)
|
21
|
+
fk = foreign_key.call(owner)
|
22
|
+
if fk.respond_to?(:each)
|
23
|
+
bind_vals = fk.compact.map do |f|
|
24
|
+
bind scope, table.table_name, key.to_s, f, tracker
|
25
|
+
end
|
26
|
+
table[key].in(bind_vals)
|
27
|
+
else
|
28
|
+
bind_val = bind scope, table.table_name, key.to_s, fk, tracker
|
29
|
+
table[key].eq(bind_val)
|
30
|
+
end
|
31
|
+
else
|
32
|
+
bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key], tracker
|
33
|
+
table[key].eq(bind_val)
|
34
|
+
end
|
35
|
+
scope = scope.where(bind_arel)
|
36
|
+
|
37
|
+
if reflection.type
|
38
|
+
value = owner.class.base_class.name
|
39
|
+
bind_val = bind scope, table.table_name, reflection.type.to_s, value, tracker
|
40
|
+
scope = scope.where(table[reflection.type].eq(bind_val))
|
41
|
+
end
|
42
|
+
else
|
43
|
+
constraint = table[key].eq(foreign_table[foreign_key])
|
44
|
+
|
45
|
+
if reflection.type
|
46
|
+
value = chain[i + 1].klass.base_class.name
|
47
|
+
bind_val = bind scope, table.table_name, reflection.type.to_s, value, tracker
|
48
|
+
scope = scope.where(table[reflection.type].eq(bind_val))
|
49
|
+
end
|
50
|
+
|
51
|
+
scope = scope.joins(join(foreign_table, constraint))
|
52
|
+
end
|
53
|
+
|
54
|
+
is_first_chain = i == 0
|
55
|
+
klass = is_first_chain ? assoc_klass : reflection.klass
|
56
|
+
|
57
|
+
# Exclude the scope of the association itself, because that
|
58
|
+
# was already merged in the #scope method.
|
59
|
+
scope_chain[i].each do |scope_chain_item|
|
60
|
+
item = eval_scope(klass, scope_chain_item, owner)
|
61
|
+
|
62
|
+
if scope_chain_item == refl.scope
|
63
|
+
scope.merge! item.except(:where, :includes, :bind)
|
64
|
+
end
|
65
|
+
|
66
|
+
if is_first_chain
|
67
|
+
scope.includes! item.includes_values
|
68
|
+
end
|
69
|
+
|
70
|
+
scope.where_values += item.where_values
|
71
|
+
scope.bind_values += item.bind_values
|
72
|
+
scope.order_values |= item.order_values
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
scope
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|