ransack 1.8.3 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +3 -0
  3. data/.travis.yml +26 -63
  4. data/CHANGELOG.md +187 -24
  5. data/CONTRIBUTING.md +9 -0
  6. data/Gemfile +5 -20
  7. data/README.md +163 -40
  8. data/Rakefile +1 -22
  9. data/lib/ransack/adapters/active_record/base.rb +11 -2
  10. data/lib/ransack/adapters/active_record/context.rb +178 -168
  11. data/lib/ransack/adapters/active_record/ransack/constants.rb +6 -3
  12. data/lib/ransack/adapters/active_record/ransack/context.rb +10 -16
  13. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +3 -3
  14. data/lib/ransack/adapters/active_record/ransack/translate.rb +1 -5
  15. data/lib/ransack/adapters/active_record/ransack/visitor.rb +23 -0
  16. data/lib/ransack/adapters/active_record.rb +0 -9
  17. data/lib/ransack/adapters.rb +2 -0
  18. data/lib/ransack/configuration.rb +30 -4
  19. data/lib/ransack/constants.rb +4 -1
  20. data/lib/ransack/context.rb +29 -24
  21. data/lib/ransack/helpers/form_builder.rb +15 -3
  22. data/lib/ransack/helpers/form_helper.rb +8 -3
  23. data/lib/ransack/locale/ar.yml +70 -0
  24. data/lib/ransack/locale/az.yml +70 -0
  25. data/lib/ransack/locale/bg.yml +70 -0
  26. data/lib/ransack/locale/ca.yml +70 -0
  27. data/lib/ransack/locale/el.yml +70 -0
  28. data/lib/ransack/locale/es.yml +22 -22
  29. data/lib/ransack/locale/fa.yml +70 -0
  30. data/lib/ransack/locale/fi.yml +71 -0
  31. data/lib/ransack/locale/it.yml +70 -0
  32. data/lib/ransack/locale/nl.yml +4 -4
  33. data/lib/ransack/locale/ru.yml +70 -0
  34. data/lib/ransack/locale/tr.yml +70 -0
  35. data/lib/ransack/locale/zh-CN.yml +12 -12
  36. data/lib/ransack/nodes/attribute.rb +1 -1
  37. data/lib/ransack/nodes/grouping.rb +2 -7
  38. data/lib/ransack/nodes/value.rb +74 -68
  39. data/lib/ransack/predicate.rb +11 -19
  40. data/lib/ransack/search.rb +1 -1
  41. data/lib/ransack/translate.rb +115 -115
  42. data/lib/ransack/version.rb +1 -1
  43. data/lib/ransack/visitor.rb +1 -12
  44. data/lib/ransack.rb +5 -2
  45. data/logo/ransack-h.png +0 -0
  46. data/logo/ransack-h.svg +34 -0
  47. data/logo/ransack-v.png +0 -0
  48. data/logo/ransack-v.svg +34 -0
  49. data/logo/ransack.png +0 -0
  50. data/logo/ransack.svg +21 -0
  51. data/polyamorous/lib/polyamorous/activerecord_5.0_ruby_2/join_association.rb +2 -0
  52. data/polyamorous/lib/polyamorous/activerecord_5.0_ruby_2/join_dependency.rb +2 -0
  53. data/polyamorous/lib/polyamorous/activerecord_5.1_ruby_2/join_association.rb +31 -0
  54. data/polyamorous/lib/polyamorous/activerecord_5.1_ruby_2/join_dependency.rb +112 -0
  55. data/polyamorous/lib/polyamorous/activerecord_5.2.0_ruby_2/join_association.rb +31 -0
  56. data/polyamorous/lib/polyamorous/activerecord_5.2.0_ruby_2/join_dependency.rb +112 -0
  57. data/polyamorous/lib/polyamorous/activerecord_5.2.0_ruby_2/reflection.rb +12 -0
  58. data/polyamorous/lib/polyamorous/activerecord_5.2.1_ruby_2/join_association.rb +22 -0
  59. data/polyamorous/lib/polyamorous/activerecord_5.2.1_ruby_2/join_dependency.rb +81 -0
  60. data/polyamorous/lib/polyamorous/activerecord_5.2.1_ruby_2/reflection.rb +2 -0
  61. data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +2 -0
  62. data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/join_dependency.rb +81 -0
  63. data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +2 -0
  64. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +2 -0
  65. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +2 -0
  66. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +2 -0
  67. data/polyamorous/lib/polyamorous/join.rb +70 -0
  68. data/polyamorous/lib/polyamorous/swapping_reflection_class.rb +11 -0
  69. data/polyamorous/lib/polyamorous/tree_node.rb +7 -0
  70. data/polyamorous/lib/polyamorous/version.rb +3 -0
  71. data/polyamorous/lib/polyamorous.rb +29 -0
  72. data/polyamorous/polyamorous.gemspec +35 -0
  73. data/ransack.gemspec +9 -10
  74. data/spec/helpers/polyamorous_helper.rb +28 -0
  75. data/spec/ransack/adapters/active_record/base_spec.rb +74 -0
  76. data/spec/ransack/adapters/active_record/context_spec.rb +44 -6
  77. data/spec/ransack/configuration_spec.rb +17 -2
  78. data/spec/ransack/helpers/form_builder_spec.rb +3 -15
  79. data/spec/ransack/helpers/form_helper_spec.rb +88 -151
  80. data/spec/ransack/join_association_spec.rb +28 -0
  81. data/spec/ransack/join_dependency_spec.rb +97 -0
  82. data/spec/ransack/join_spec.rb +19 -0
  83. data/spec/ransack/predicate_spec.rb +16 -2
  84. data/spec/ransack/search_spec.rb +32 -3
  85. data/spec/spec_helper.rb +5 -0
  86. data/spec/support/schema.rb +45 -21
  87. metadata +81 -67
  88. data/lib/ransack/adapters/active_record/3.0/compat.rb +0 -179
  89. data/lib/ransack/adapters/active_record/3.0/context.rb +0 -203
  90. data/lib/ransack/adapters/active_record/3.1/context.rb +0 -212
  91. data/lib/ransack/adapters/active_record/3.2/context.rb +0 -44
  92. data/lib/ransack/adapters/active_record/compat.rb +0 -14
  93. data/lib/ransack/adapters/mongoid/3.2/.gitkeep +0 -0
  94. data/lib/ransack/adapters/mongoid/attributes/attribute.rb +0 -37
  95. data/lib/ransack/adapters/mongoid/attributes/order_predications.rb +0 -17
  96. data/lib/ransack/adapters/mongoid/attributes/predications.rb +0 -141
  97. data/lib/ransack/adapters/mongoid/base.rb +0 -134
  98. data/lib/ransack/adapters/mongoid/context.rb +0 -212
  99. data/lib/ransack/adapters/mongoid/inquiry_hash.rb +0 -23
  100. data/lib/ransack/adapters/mongoid/ransack/constants.rb +0 -88
  101. data/lib/ransack/adapters/mongoid/ransack/context.rb +0 -60
  102. data/lib/ransack/adapters/mongoid/ransack/nodes/condition.rb +0 -27
  103. data/lib/ransack/adapters/mongoid/ransack/translate.rb +0 -13
  104. data/lib/ransack/adapters/mongoid/ransack/visitor.rb +0 -24
  105. data/lib/ransack/adapters/mongoid/table.rb +0 -35
  106. data/lib/ransack/adapters/mongoid.rb +0 -15
  107. data/spec/mongoid/adapters/mongoid/base_spec.rb +0 -314
  108. data/spec/mongoid/adapters/mongoid/context_spec.rb +0 -56
  109. data/spec/mongoid/configuration_spec.rb +0 -162
  110. data/spec/mongoid/dependencies_spec.rb +0 -8
  111. data/spec/mongoid/helpers/ransack_helper.rb +0 -11
  112. data/spec/mongoid/nodes/condition_spec.rb +0 -49
  113. data/spec/mongoid/nodes/grouping_spec.rb +0 -13
  114. data/spec/mongoid/predicate_spec.rb +0 -155
  115. data/spec/mongoid/search_spec.rb +0 -445
  116. data/spec/mongoid/support/mongoid.yml +0 -11
  117. data/spec/mongoid/support/schema.rb +0 -135
  118. data/spec/mongoid/translate_spec.rb +0 -14
  119. data/spec/mongoid_spec_helper.rb +0 -63
  120. data/spec/ransack/dependencies_spec.rb +0 -12
data/logo/ransack.svg ADDED
@@ -0,0 +1,21 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
+ <svg width="100%" height="100%" viewBox="0 0 401 417" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
4
+ <g transform="matrix(1,0,0,1,-199.992,-191.649)">
5
+ <g transform="matrix(3.50044,0,0,3.50044,-3413.33,-5715.71)">
6
+ <path d="M1032.25,1689C1032.25,1688.23 1032.87,1687.61 1033.64,1687.61C1042.86,1687.61 1094.52,1687.61 1094.52,1687.61L1094.52,1687.61C1117.36,1687.65 1135.89,1706.2 1135.89,1729.05C1135.89,1741.48 1128.4,1752.63 1119.73,1760.23C1119.73,1760.23 1141.26,1797.52 1145.72,1805.26C1145.88,1805.54 1145.88,1805.9 1145.72,1806.18C1145.55,1806.47 1145.25,1806.65 1144.91,1806.65C1137.59,1806.65 1107.52,1806.65 1103.12,1806.65C1102.79,1806.65 1102.49,1806.47 1102.32,1806.18C1099.89,1801.97 1081.71,1770.49 1081.71,1770.49L1073.76,1770.49C1073.76,1770.49 1073.76,1799.1 1073.76,1805.42C1073.76,1806.1 1073.21,1806.65 1072.54,1806.65C1066.45,1806.65 1039.56,1806.65 1033.47,1806.65C1032.8,1806.65 1032.25,1806.1 1032.25,1805.42C1032.25,1793.69 1032.25,1701.48 1032.25,1689Z" style="fill:url(#_Linear1);"/>
7
+ <clipPath id="_clip2">
8
+ <path d="M1032.25,1689C1032.25,1688.23 1032.87,1687.61 1033.64,1687.61C1042.86,1687.61 1094.52,1687.61 1094.52,1687.61L1094.52,1687.61C1117.36,1687.65 1135.89,1706.2 1135.89,1729.05C1135.89,1741.48 1128.4,1752.63 1119.73,1760.23C1119.73,1760.23 1141.26,1797.52 1145.72,1805.26C1145.88,1805.54 1145.88,1805.9 1145.72,1806.18C1145.55,1806.47 1145.25,1806.65 1144.91,1806.65C1137.59,1806.65 1107.52,1806.65 1103.12,1806.65C1102.79,1806.65 1102.49,1806.47 1102.32,1806.18C1099.89,1801.97 1081.71,1770.49 1081.71,1770.49L1073.76,1770.49C1073.76,1770.49 1073.76,1799.1 1073.76,1805.42C1073.76,1806.1 1073.21,1806.65 1072.54,1806.65C1066.45,1806.65 1039.56,1806.65 1033.47,1806.65C1032.8,1806.65 1032.25,1806.1 1032.25,1805.42C1032.25,1793.69 1032.25,1701.48 1032.25,1689Z"/>
9
+ </clipPath>
10
+ <g clip-path="url(#_clip2)">
11
+ <g transform="matrix(1.05537,0,0,1.05537,-56.2001,-93.6352)">
12
+ <path d="M1090.07,1757.11C1087.62,1757.78 1085.05,1758.14 1082.39,1758.14C1066.34,1758.14 1053.3,1745.1 1053.3,1729.05C1053.3,1712.99 1066.34,1699.96 1082.39,1699.96C1098.45,1699.96 1111.49,1712.99 1111.49,1729.05C1111.49,1738.8 1106.68,1747.44 1099.3,1752.72C1099.3,1752.72 1110.29,1771.75 1116.66,1782.78C1117.57,1784.36 1117.57,1786.31 1116.66,1787.89C1115.75,1789.47 1114.06,1790.44 1112.23,1790.44C1112.23,1790.44 1112.23,1790.44 1112.23,1790.44C1110.43,1790.44 1108.76,1789.48 1107.86,1787.92C1103.37,1780.15 1090.07,1757.11 1090.07,1757.11ZM1082.52,1708.18C1084.31,1708.32 1084.32,1708.88 1084.66,1709.62C1085.4,1711.22 1084.12,1713.13 1081.99,1713.18C1073.82,1713.44 1066.55,1720.54 1066.53,1729.05C1066.53,1729.05 1066.41,1730.38 1065.53,1731.04C1063.81,1732.34 1061.32,1730.94 1061.59,1727.44C1062.36,1717.22 1071.49,1708.34 1082.13,1708.18C1082.26,1708.18 1082.39,1708.18 1082.52,1708.18Z" style="fill:rgb(52,52,107);fill-opacity:0.4;"/>
13
+ </g>
14
+ <path d="M1090.07,1757.11C1087.62,1757.78 1085.05,1758.14 1082.39,1758.14C1066.34,1758.14 1053.3,1745.1 1053.3,1729.05C1053.3,1712.99 1066.34,1699.96 1082.39,1699.96C1098.45,1699.96 1111.49,1712.99 1111.49,1729.05C1111.49,1738.8 1106.68,1747.44 1099.3,1752.72C1099.3,1752.72 1110.19,1771.58 1116.57,1782.63C1117.5,1784.24 1117.5,1786.23 1116.57,1787.84C1115.64,1789.45 1113.92,1790.44 1112.06,1790.44C1112.06,1790.44 1112.06,1790.44 1112.06,1790.44C1110.36,1790.44 1108.79,1789.54 1107.94,1788.07C1103.54,1780.44 1090.07,1757.11 1090.07,1757.11ZM1082.52,1708.18C1084.31,1708.32 1084.32,1708.88 1084.66,1709.62C1085.4,1711.22 1084.12,1713.13 1081.99,1713.18C1073.82,1713.44 1066.55,1720.54 1066.53,1729.05C1066.53,1729.05 1066.41,1730.38 1065.53,1731.04C1063.81,1732.34 1061.32,1730.94 1061.59,1727.44C1062.36,1717.22 1071.49,1708.34 1082.13,1708.18C1082.26,1708.18 1082.39,1708.18 1082.52,1708.18Z" style="fill:rgb(251,248,255);"/>
15
+ </g>
16
+ </g>
17
+ </g>
18
+ <defs>
19
+ <linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(121.538,125.482,-125.482,121.538,1032.25,1687.61)"><stop offset="0" style="stop-color:rgb(132,132,220);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(51,51,180);stop-opacity:1"/></linearGradient>
20
+ </defs>
21
+ </svg>
@@ -0,0 +1,2 @@
1
+ # active_record_5.0_ruby_2/join_association.rb
2
+ require 'polyamorous/activerecord_5.1_ruby_2/join_association'
@@ -0,0 +1,2 @@
1
+ # active_record_5.0_ruby_2/join_dependency.rb
2
+ require 'polyamorous/activerecord_5.1_ruby_2/join_dependency'
@@ -0,0 +1,31 @@
1
+ # active_record_5.1_ruby_2/join_association.rb
2
+
3
+ module Polyamorous
4
+ module JoinAssociationExtensions
5
+ include SwappingReflectionClass
6
+ def self.prepended(base)
7
+ base.class_eval { attr_reader :join_type }
8
+ end
9
+
10
+ def initialize(reflection, children, polymorphic_class = nil, 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
+ def build_constraint(klass, table, key, foreign_table, foreign_key)
23
+ if reflection.polymorphic?
24
+ super(klass, table, key, foreign_table, foreign_key)
25
+ .and(foreign_table[reflection.foreign_type].eq(reflection.klass.name))
26
+ else
27
+ super(klass, table, key, foreign_table, foreign_key)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,112 @@
1
+ # active_record_5.1_ruby_2/join_dependency.rb
2
+
3
+ module Polyamorous
4
+ module JoinDependencyExtensions
5
+ # Replaces ActiveRecord::Associations::JoinDependency#build
6
+ #
7
+ def build(associations, base_klass)
8
+ associations.map do |name, right|
9
+ if name.is_a? Join
10
+ reflection = find_reflection base_klass, name.name
11
+ reflection.check_validity!
12
+ reflection.check_eager_loadable!
13
+
14
+ klass = if reflection.polymorphic?
15
+ name.klass || base_klass
16
+ else
17
+ reflection.klass
18
+ end
19
+ JoinAssociation.new(reflection, build(right, klass), name.klass, name.type)
20
+ else
21
+ reflection = find_reflection base_klass, name
22
+ reflection.check_validity!
23
+ reflection.check_eager_loadable!
24
+
25
+ if reflection.polymorphic?
26
+ raise ActiveRecord::EagerLoadPolymorphicError.new(reflection)
27
+ end
28
+ JoinAssociation.new reflection, build(right, reflection.klass)
29
+ end
30
+ end
31
+ end
32
+
33
+ # Replaces ActiveRecord::Associations::JoinDependency#join_constraints
34
+ #
35
+ # This internal method was changed in Rails 5.0 by commit
36
+ # https://github.com/rails/rails/commit/e038975 which added
37
+ # left_outer_joins (see #make_polyamorous_left_outer_joins below) and added
38
+ # passing an additional argument, `join_type`, to #join_constraints.
39
+ #
40
+ def join_constraints(outer_joins, join_type)
41
+ joins = join_root.children.flat_map { |child|
42
+ if join_type == Arel::Nodes::OuterJoin
43
+ make_polyamorous_left_outer_joins join_root, child
44
+ else
45
+ make_polyamorous_inner_joins join_root, child
46
+ end
47
+ }
48
+
49
+ joins.concat outer_joins.flat_map { |oj|
50
+ if join_root.match? oj.join_root
51
+ walk(join_root, oj.join_root)
52
+ else
53
+ oj.join_root.children.flat_map { |child|
54
+ make_outer_joins(oj.join_root, child)
55
+ }
56
+ end
57
+ }
58
+ end
59
+
60
+ # Replaces ActiveRecord::Associations::JoinDependency#make_left_outer_joins,
61
+ # a new method that was added in Rails 5.0 with the following commit:
62
+ # https://github.com/rails/rails/commit/e038975
63
+ #
64
+ def make_polyamorous_left_outer_joins(parent, child)
65
+ tables = child.tables
66
+ join_type = Arel::Nodes::OuterJoin
67
+ info = make_constraints parent, child, tables, join_type
68
+
69
+ [info] + child.children.flat_map { |c|
70
+ make_polyamorous_left_outer_joins(child, c)
71
+ }
72
+ end
73
+
74
+ # Replaces ActiveRecord::Associations::JoinDependency#make_inner_joins
75
+ #
76
+ def make_polyamorous_inner_joins(parent, child)
77
+ tables = child.tables
78
+ join_type = child.join_type || Arel::Nodes::InnerJoin
79
+ info = make_constraints parent, child, tables, join_type
80
+
81
+ [info] + child.children.flat_map { |c|
82
+ make_polyamorous_inner_joins(child, c)
83
+ }
84
+ end
85
+
86
+ private :make_polyamorous_inner_joins, :make_polyamorous_left_outer_joins
87
+
88
+ module ClassMethods
89
+ # Prepended before ActiveRecord::Associations::JoinDependency#walk_tree
90
+ #
91
+ def walk_tree(associations, hash)
92
+ case associations
93
+ when TreeNode
94
+ associations.add_to_tree(hash)
95
+ when Hash
96
+ associations.each do |k, v|
97
+ cache =
98
+ if TreeNode === k
99
+ k.add_to_tree(hash)
100
+ else
101
+ hash[k] ||= {}
102
+ end
103
+ walk_tree(v, cache)
104
+ end
105
+ else
106
+ super(associations, hash)
107
+ end
108
+ end
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,31 @@
1
+ # active_record_5.2_ruby_2/join_association.rb
2
+
3
+ module Polyamorous
4
+ module JoinAssociationExtensions
5
+ include SwappingReflectionClass
6
+ def self.prepended(base)
7
+ base.class_eval { attr_reader :join_type }
8
+ end
9
+
10
+ def initialize(reflection, children, alias_tracker, polymorphic_class = nil, 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, alias_tracker)
15
+ self.reflection.options[:polymorphic] = true
16
+ end
17
+ else
18
+ super(reflection, children, alias_tracker)
19
+ end
20
+ end
21
+
22
+ def build_constraint(klass, table, key, foreign_table, foreign_key)
23
+ if reflection.polymorphic?
24
+ super(klass, table, key, foreign_table, foreign_key)
25
+ .and(foreign_table[reflection.foreign_type].eq(reflection.klass.name))
26
+ else
27
+ super(klass, table, key, foreign_table, foreign_key)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,112 @@
1
+ # active_record_5.2_ruby_2/join_dependency.rb
2
+
3
+ module Polyamorous
4
+ module JoinDependencyExtensions
5
+ # Replaces ActiveRecord::Associations::JoinDependency#build
6
+ #
7
+ def build(associations, base_klass)
8
+ associations.map do |name, right|
9
+ if name.is_a? Join
10
+ reflection = find_reflection base_klass, name.name
11
+ reflection.check_validity!
12
+ reflection.check_eager_loadable!
13
+
14
+ klass = if reflection.polymorphic?
15
+ name.klass || base_klass
16
+ else
17
+ reflection.klass
18
+ end
19
+ JoinAssociation.new(reflection, build(right, klass), alias_tracker, name.klass, name.type)
20
+ else
21
+ reflection = find_reflection base_klass, name
22
+ reflection.check_validity!
23
+ reflection.check_eager_loadable!
24
+
25
+ if reflection.polymorphic?
26
+ raise ActiveRecord::EagerLoadPolymorphicError.new(reflection)
27
+ end
28
+ JoinAssociation.new(reflection, build(right, reflection.klass), alias_tracker)
29
+ end
30
+ end
31
+ end
32
+
33
+ # Replaces ActiveRecord::Associations::JoinDependency#join_constraints
34
+ #
35
+ # This internal method was changed in Rails 5.0 by commit
36
+ # https://github.com/rails/rails/commit/e038975 which added
37
+ # left_outer_joins (see #make_polyamorous_left_outer_joins below) and added
38
+ # passing an additional argument, `join_type`, to #join_constraints.
39
+ #
40
+ def join_constraints(outer_joins, join_type)
41
+ joins = join_root.children.flat_map { |child|
42
+ if join_type == Arel::Nodes::OuterJoin
43
+ make_polyamorous_left_outer_joins join_root, child
44
+ else
45
+ make_polyamorous_inner_joins join_root, child
46
+ end
47
+ }
48
+
49
+ joins.concat outer_joins.flat_map { |oj|
50
+ if join_root.match?(oj.join_root) && join_root.table.name == oj.join_root.table.name
51
+ walk(join_root, oj.join_root)
52
+ else
53
+ oj.join_root.children.flat_map { |child|
54
+ make_outer_joins(oj.join_root, child)
55
+ }
56
+ end
57
+ }
58
+ end
59
+
60
+ # Replaces ActiveRecord::Associations::JoinDependency#make_left_outer_joins,
61
+ # a new method that was added in Rails 5.0 with the following commit:
62
+ # https://github.com/rails/rails/commit/e038975
63
+ #
64
+ def make_polyamorous_left_outer_joins(parent, child)
65
+ tables = child.tables
66
+ join_type = Arel::Nodes::OuterJoin
67
+ info = make_constraints parent, child, tables, join_type
68
+
69
+ info + child.children.flat_map { |c|
70
+ make_polyamorous_left_outer_joins(child, c)
71
+ }
72
+ end
73
+
74
+ # Replaces ActiveRecord::Associations::JoinDependency#make_inner_joins
75
+ #
76
+ def make_polyamorous_inner_joins(parent, child)
77
+ tables = child.tables
78
+ join_type = child.join_type || Arel::Nodes::InnerJoin
79
+ info = make_constraints parent, child, tables, join_type
80
+
81
+ info + child.children.flat_map { |c|
82
+ make_polyamorous_inner_joins(child, c)
83
+ }
84
+ end
85
+
86
+ private :make_polyamorous_inner_joins, :make_polyamorous_left_outer_joins
87
+
88
+ module ClassMethods
89
+ # Prepended before ActiveRecord::Associations::JoinDependency#walk_tree
90
+ #
91
+ def walk_tree(associations, hash)
92
+ case associations
93
+ when TreeNode
94
+ associations.add_to_tree(hash)
95
+ when Hash
96
+ associations.each do |k, v|
97
+ cache =
98
+ if TreeNode === k
99
+ k.add_to_tree(hash)
100
+ else
101
+ hash[k] ||= {}
102
+ end
103
+ walk_tree(v, cache)
104
+ end
105
+ else
106
+ super(associations, hash)
107
+ end
108
+ end
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,12 @@
1
+ module Polyamorous
2
+ module ReflectionExtensions
3
+ def build_join_constraint(table, foreign_table)
4
+ if polymorphic?
5
+ super(table, foreign_table)
6
+ .and(foreign_table[foreign_type].eq(klass.name))
7
+ else
8
+ super(table, foreign_table)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,22 @@
1
+ # active_record_5.2.1_ruby_2/join_association.rb
2
+
3
+ module Polyamorous
4
+ module JoinAssociationExtensions
5
+ include SwappingReflectionClass
6
+ def self.prepended(base)
7
+ base.class_eval { attr_reader :join_type }
8
+ end
9
+
10
+ def initialize(reflection, children, polymorphic_class = nil, 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
+ end
22
+ end
@@ -0,0 +1,81 @@
1
+ # active_record_5.2.1_ruby_2/join_dependency.rb
2
+
3
+ module Polyamorous
4
+ module JoinDependencyExtensions
5
+ # Replaces ActiveRecord::Associations::JoinDependency#build
6
+ def build(associations, base_klass)
7
+ associations.map do |name, right|
8
+ if name.is_a? Join
9
+ reflection = find_reflection base_klass, name.name
10
+ reflection.check_validity!
11
+ reflection.check_eager_loadable!
12
+
13
+ klass = if reflection.polymorphic?
14
+ name.klass || base_klass
15
+ else
16
+ reflection.klass
17
+ end
18
+ JoinAssociation.new(reflection, build(right, klass), name.klass, name.type)
19
+ else
20
+ reflection = find_reflection base_klass, name
21
+ reflection.check_validity!
22
+ reflection.check_eager_loadable!
23
+
24
+ if reflection.polymorphic?
25
+ raise ActiveRecord::EagerLoadPolymorphicError.new(reflection)
26
+ end
27
+ JoinAssociation.new(reflection, build(right, reflection.klass))
28
+ end
29
+ end
30
+ end
31
+
32
+ def join_constraints(joins_to_add, join_type, alias_tracker)
33
+ @alias_tracker = alias_tracker
34
+
35
+ construct_tables!(join_root)
36
+ joins = make_join_constraints(join_root, join_type)
37
+
38
+ joins.concat joins_to_add.flat_map { |oj|
39
+ construct_tables!(oj.join_root)
40
+ if join_root.match?(oj.join_root) && join_root.table.name == oj.join_root.table.name
41
+ walk join_root, oj.join_root
42
+ else
43
+ make_join_constraints(oj.join_root, join_type)
44
+ end
45
+ }
46
+ end
47
+
48
+ private
49
+ def make_constraints(parent, child, join_type = Arel::Nodes::OuterJoin)
50
+ foreign_table = parent.table
51
+ foreign_klass = parent.base_klass
52
+ join_type = child.join_type || join_type if join_type == Arel::Nodes::InnerJoin
53
+ joins = child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
54
+ joins.concat child.children.flat_map { |c| make_constraints(child, c, join_type) }
55
+ end
56
+
57
+ module ClassMethods
58
+ # Prepended before ActiveRecord::Associations::JoinDependency#walk_tree
59
+ #
60
+ def walk_tree(associations, hash)
61
+ case associations
62
+ when TreeNode
63
+ associations.add_to_tree(hash)
64
+ when Hash
65
+ associations.each do |k, v|
66
+ cache =
67
+ if TreeNode === k
68
+ k.add_to_tree(hash)
69
+ else
70
+ hash[k] ||= {}
71
+ end
72
+ walk_tree(v, cache)
73
+ end
74
+ else
75
+ super(associations, hash)
76
+ end
77
+ end
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,2 @@
1
+ # active_record_5.2.1_ruby_2/reflection.rb
2
+ require 'polyamorous/activerecord_5.2.0_ruby_2/reflection'
@@ -0,0 +1,2 @@
1
+ # active_record_6.0_ruby_2/join_association
2
+ require 'polyamorous/activerecord_5.2.1_ruby_2/join_association'
@@ -0,0 +1,81 @@
1
+ # active_record_6.0_ruby_2/join_dependency.rb
2
+
3
+ module Polyamorous
4
+ module JoinDependencyExtensions
5
+ # Replaces ActiveRecord::Associations::JoinDependency#build
6
+ def build(associations, base_klass)
7
+ associations.map do |name, right|
8
+ if name.is_a? Join
9
+ reflection = find_reflection base_klass, name.name
10
+ reflection.check_validity!
11
+ reflection.check_eager_loadable!
12
+
13
+ klass = if reflection.polymorphic?
14
+ name.klass || base_klass
15
+ else
16
+ reflection.klass
17
+ end
18
+ JoinAssociation.new(reflection, build(right, klass), name.klass, name.type)
19
+ else
20
+ reflection = find_reflection base_klass, name
21
+ reflection.check_validity!
22
+ reflection.check_eager_loadable!
23
+
24
+ if reflection.polymorphic?
25
+ raise ActiveRecord::EagerLoadPolymorphicError.new(reflection)
26
+ end
27
+ JoinAssociation.new(reflection, build(right, reflection.klass))
28
+ end
29
+ end
30
+ end
31
+
32
+ def join_constraints(joins_to_add, alias_tracker)
33
+ @alias_tracker = alias_tracker
34
+
35
+ construct_tables!(join_root)
36
+ joins = make_join_constraints(join_root, join_type)
37
+
38
+ joins.concat joins_to_add.flat_map { |oj|
39
+ construct_tables!(oj.join_root)
40
+ if join_root.match?(oj.join_root) && join_root.table.name == oj.join_root.table.name
41
+ walk join_root, oj.join_root, oj.join_type
42
+ else
43
+ make_join_constraints(oj.join_root, oj.join_type)
44
+ end
45
+ }
46
+ end
47
+
48
+ private
49
+ def make_constraints(parent, child, join_type = Arel::Nodes::OuterJoin)
50
+ foreign_table = parent.table
51
+ foreign_klass = parent.base_klass
52
+ join_type = child.join_type || join_type if join_type == Arel::Nodes::InnerJoin
53
+ joins = child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
54
+ joins.concat child.children.flat_map { |c| make_constraints(child, c, join_type) }
55
+ end
56
+
57
+ module ClassMethods
58
+ # Prepended before ActiveRecord::Associations::JoinDependency#walk_tree
59
+ #
60
+ def walk_tree(associations, hash)
61
+ case associations
62
+ when TreeNode
63
+ associations.add_to_tree(hash)
64
+ when Hash
65
+ associations.each do |k, v|
66
+ cache =
67
+ if TreeNode === k
68
+ k.add_to_tree(hash)
69
+ else
70
+ hash[k] ||= {}
71
+ end
72
+ walk_tree(v, cache)
73
+ end
74
+ else
75
+ super(associations, hash)
76
+ end
77
+ end
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,2 @@
1
+ # active_record_6.0_ruby_2/reflection.rb
2
+ require 'polyamorous/activerecord_5.2.0_ruby_2/reflection'
@@ -0,0 +1,2 @@
1
+ # active_record_6.1_ruby_2/join_association
2
+ require 'polyamorous/activerecord_6.0_ruby_2/join_association'
@@ -0,0 +1,2 @@
1
+ # active_record_6.1_ruby_2/join_dependency.rb
2
+ require 'polyamorous/activerecord_6.0_ruby_2/join_dependency'
@@ -0,0 +1,2 @@
1
+ # active_record_6.1_ruby_2/reflection.rb
2
+ require 'polyamorous/activerecord_6.0_ruby_2/reflection'
@@ -0,0 +1,70 @@
1
+ module Polyamorous
2
+ class Join
3
+ include TreeNode
4
+
5
+ attr_accessor :name
6
+ attr_reader :type, :klass
7
+
8
+ def initialize(name, type = InnerJoin, klass = nil)
9
+ @name = name
10
+ @type = convert_to_arel_join_type(type)
11
+ @klass = convert_to_class(klass) if klass
12
+ end
13
+
14
+ def klass=(klass)
15
+ @klass = convert_to_class(klass) if klass
16
+ end
17
+
18
+ def type=(type)
19
+ @type = convert_to_arel_join_type(type) if type
20
+ end
21
+
22
+ def hash
23
+ [@name, @type, @klass].hash
24
+ end
25
+
26
+ def eql?(other)
27
+ self.class == other.class &&
28
+ self.name == other.name &&
29
+ self.type == other.type &&
30
+ self.klass == other.klass
31
+ end
32
+
33
+ alias :== :eql?
34
+
35
+ def add_to_tree(hash)
36
+ hash[self] ||= {}
37
+ end
38
+
39
+ private
40
+
41
+ def convert_to_arel_join_type(type)
42
+ case type
43
+ when 'inner', :inner
44
+ InnerJoin
45
+ when 'outer', :outer
46
+ OuterJoin
47
+ when Class
48
+ if [InnerJoin, OuterJoin].include? type
49
+ type
50
+ else
51
+ raise ArgumentError, "#{type} cannot be converted to an ARel join type"
52
+ end
53
+ else
54
+ raise ArgumentError, "#{type} cannot be converted to an ARel join type"
55
+ end
56
+ end
57
+
58
+ def convert_to_class(value)
59
+ case value
60
+ when String, Symbol
61
+ Kernel.const_get(value)
62
+ when Class
63
+ value
64
+ else
65
+ raise ArgumentError, "#{value} cannot be converted to a Class"
66
+ end
67
+ end
68
+
69
+ end
70
+ 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
@@ -0,0 +1,7 @@
1
+ module Polyamorous
2
+ module TreeNode
3
+ def add_to_tree(hash)
4
+ raise NotImplementedError
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module Polyamorous
2
+ VERSION = '2.3.0'
3
+ end
@@ -0,0 +1,29 @@
1
+ if defined?(::ActiveRecord)
2
+ module Polyamorous
3
+ InnerJoin = Arel::Nodes::InnerJoin
4
+ OuterJoin = Arel::Nodes::OuterJoin
5
+
6
+ JoinDependency = ::ActiveRecord::Associations::JoinDependency
7
+ JoinAssociation = ::ActiveRecord::Associations::JoinDependency::JoinAssociation
8
+ end
9
+
10
+ require 'polyamorous/tree_node'
11
+ require 'polyamorous/join'
12
+ require 'polyamorous/swapping_reflection_class'
13
+
14
+ ar_version = ::ActiveRecord::VERSION::STRING[0,3]
15
+ ar_version = ::ActiveRecord::VERSION::STRING[0,5] if ar_version >= "5.2" && ::ActiveRecord.version < ::Gem::Version.new("6.0")
16
+ ar_version = "5.2.1" if ::ActiveRecord::VERSION::STRING >= "5.2.1" && ::ActiveRecord.version < ::Gem::Version.new("6.0")
17
+ %w(join_association join_dependency).each do |file|
18
+ require "polyamorous/activerecord_#{ar_version}_ruby_2/#{file}"
19
+ end
20
+
21
+ if ar_version >= "5.2.0"
22
+ require "polyamorous/activerecord_#{ar_version}_ruby_2/reflection.rb"
23
+ ::ActiveRecord::Reflection::AbstractReflection.send(:prepend, Polyamorous::ReflectionExtensions)
24
+ end
25
+
26
+ Polyamorous::JoinDependency.send(:prepend, Polyamorous::JoinDependencyExtensions)
27
+ Polyamorous::JoinDependency.singleton_class.send(:prepend, Polyamorous::JoinDependencyExtensions::ClassMethods)
28
+ Polyamorous::JoinAssociation.send(:prepend, Polyamorous::JoinAssociationExtensions)
29
+ end