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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +14 -0
  5. data/Appraisals +18 -0
  6. data/Gemfile +14 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +66 -0
  9. data/Rakefile +18 -0
  10. data/certs/razum2um.pem +21 -0
  11. data/gemfiles/.gitignore +1 -0
  12. data/gemfiles/rails_3_2.gemfile +14 -0
  13. data/gemfiles/rails_4_0.gemfile +14 -0
  14. data/gemfiles/rails_4_1.gemfile +14 -0
  15. data/gemfiles/rails_4_2.gemfile +17 -0
  16. data/includes_many.gemspec +30 -0
  17. data/lib/includes_many.rb +19 -0
  18. data/lib/includes_many/active_record32.rb +4 -0
  19. data/lib/includes_many/active_record32/association_scope.rb +93 -0
  20. data/lib/includes_many/active_record32/has_many.rb +43 -0
  21. data/lib/includes_many/active_record32/join_association.rb +37 -0
  22. data/lib/includes_many/active_record40.rb +4 -0
  23. data/lib/includes_many/active_record40/association_scope.rb +97 -0
  24. data/lib/includes_many/active_record40/has_many.rb +32 -0
  25. data/lib/includes_many/active_record40/join_association.rb +35 -0
  26. data/lib/includes_many/active_record41.rb +4 -0
  27. data/lib/includes_many/active_record41/association_scope.rb +88 -0
  28. data/lib/includes_many/active_record41/has_many.rb +34 -0
  29. data/lib/includes_many/active_record41/join_association.rb +30 -0
  30. data/lib/includes_many/active_record42.rb +4 -0
  31. data/lib/includes_many/active_record42/association_scope.rb +81 -0
  32. data/lib/includes_many/active_record42/has_many.rb +34 -0
  33. data/lib/includes_many/active_record42/join_association.rb +30 -0
  34. data/lib/includes_many/version.rb +3 -0
  35. data/spec/includes_many_spec.rb +83 -0
  36. data/spec/internal/app/models/comment.rb +12 -0
  37. data/spec/internal/config/database.yml +9 -0
  38. data/spec/internal/db/schema.rb +6 -0
  39. data/spec/internal/log/.gitkeep +0 -0
  40. data/spec/spec_helper.rb +26 -0
  41. 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,4 @@
1
+ require 'includes_many/active_record40/association_scope'
2
+ require 'includes_many/active_record40/has_many'
3
+ require 'includes_many/active_record40/join_association'
4
+
@@ -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,4 @@
1
+ require 'includes_many/active_record41/association_scope'
2
+ require 'includes_many/active_record41/has_many'
3
+ require 'includes_many/active_record41/join_association'
4
+
@@ -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,4 @@
1
+ require 'includes_many/active_record42/association_scope'
2
+ require 'includes_many/active_record42/has_many'
3
+ require 'includes_many/active_record42/join_association'
4
+
@@ -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
+