ransack 1.8.3 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +3 -0
- data/.travis.yml +26 -63
- data/CHANGELOG.md +187 -24
- data/CONTRIBUTING.md +9 -0
- data/Gemfile +5 -20
- data/README.md +163 -40
- data/Rakefile +1 -22
- data/lib/ransack/adapters/active_record/base.rb +11 -2
- data/lib/ransack/adapters/active_record/context.rb +178 -168
- data/lib/ransack/adapters/active_record/ransack/constants.rb +6 -3
- data/lib/ransack/adapters/active_record/ransack/context.rb +10 -16
- data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +3 -3
- data/lib/ransack/adapters/active_record/ransack/translate.rb +1 -5
- data/lib/ransack/adapters/active_record/ransack/visitor.rb +23 -0
- data/lib/ransack/adapters/active_record.rb +0 -9
- data/lib/ransack/adapters.rb +2 -0
- data/lib/ransack/configuration.rb +30 -4
- data/lib/ransack/constants.rb +4 -1
- data/lib/ransack/context.rb +29 -24
- data/lib/ransack/helpers/form_builder.rb +15 -3
- data/lib/ransack/helpers/form_helper.rb +8 -3
- data/lib/ransack/locale/ar.yml +70 -0
- data/lib/ransack/locale/az.yml +70 -0
- data/lib/ransack/locale/bg.yml +70 -0
- data/lib/ransack/locale/ca.yml +70 -0
- data/lib/ransack/locale/el.yml +70 -0
- data/lib/ransack/locale/es.yml +22 -22
- data/lib/ransack/locale/fa.yml +70 -0
- data/lib/ransack/locale/fi.yml +71 -0
- data/lib/ransack/locale/it.yml +70 -0
- data/lib/ransack/locale/nl.yml +4 -4
- data/lib/ransack/locale/ru.yml +70 -0
- data/lib/ransack/locale/tr.yml +70 -0
- data/lib/ransack/locale/zh-CN.yml +12 -12
- data/lib/ransack/nodes/attribute.rb +1 -1
- data/lib/ransack/nodes/grouping.rb +2 -7
- data/lib/ransack/nodes/value.rb +74 -68
- data/lib/ransack/predicate.rb +11 -19
- data/lib/ransack/search.rb +1 -1
- data/lib/ransack/translate.rb +115 -115
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack/visitor.rb +1 -12
- data/lib/ransack.rb +5 -2
- data/logo/ransack-h.png +0 -0
- data/logo/ransack-h.svg +34 -0
- data/logo/ransack-v.png +0 -0
- data/logo/ransack-v.svg +34 -0
- data/logo/ransack.png +0 -0
- data/logo/ransack.svg +21 -0
- data/polyamorous/lib/polyamorous/activerecord_5.0_ruby_2/join_association.rb +2 -0
- data/polyamorous/lib/polyamorous/activerecord_5.0_ruby_2/join_dependency.rb +2 -0
- data/polyamorous/lib/polyamorous/activerecord_5.1_ruby_2/join_association.rb +31 -0
- data/polyamorous/lib/polyamorous/activerecord_5.1_ruby_2/join_dependency.rb +112 -0
- data/polyamorous/lib/polyamorous/activerecord_5.2.0_ruby_2/join_association.rb +31 -0
- data/polyamorous/lib/polyamorous/activerecord_5.2.0_ruby_2/join_dependency.rb +112 -0
- data/polyamorous/lib/polyamorous/activerecord_5.2.0_ruby_2/reflection.rb +12 -0
- data/polyamorous/lib/polyamorous/activerecord_5.2.1_ruby_2/join_association.rb +22 -0
- data/polyamorous/lib/polyamorous/activerecord_5.2.1_ruby_2/join_dependency.rb +81 -0
- data/polyamorous/lib/polyamorous/activerecord_5.2.1_ruby_2/reflection.rb +2 -0
- data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +2 -0
- data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/join_dependency.rb +81 -0
- data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +2 -0
- data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +2 -0
- data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +2 -0
- data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +2 -0
- data/polyamorous/lib/polyamorous/join.rb +70 -0
- data/polyamorous/lib/polyamorous/swapping_reflection_class.rb +11 -0
- data/polyamorous/lib/polyamorous/tree_node.rb +7 -0
- data/polyamorous/lib/polyamorous/version.rb +3 -0
- data/polyamorous/lib/polyamorous.rb +29 -0
- data/polyamorous/polyamorous.gemspec +35 -0
- data/ransack.gemspec +9 -10
- data/spec/helpers/polyamorous_helper.rb +28 -0
- data/spec/ransack/adapters/active_record/base_spec.rb +74 -0
- data/spec/ransack/adapters/active_record/context_spec.rb +44 -6
- data/spec/ransack/configuration_spec.rb +17 -2
- data/spec/ransack/helpers/form_builder_spec.rb +3 -15
- data/spec/ransack/helpers/form_helper_spec.rb +88 -151
- data/spec/ransack/join_association_spec.rb +28 -0
- data/spec/ransack/join_dependency_spec.rb +97 -0
- data/spec/ransack/join_spec.rb +19 -0
- data/spec/ransack/predicate_spec.rb +16 -2
- data/spec/ransack/search_spec.rb +32 -3
- data/spec/spec_helper.rb +5 -0
- data/spec/support/schema.rb +45 -21
- metadata +81 -67
- data/lib/ransack/adapters/active_record/3.0/compat.rb +0 -179
- data/lib/ransack/adapters/active_record/3.0/context.rb +0 -203
- data/lib/ransack/adapters/active_record/3.1/context.rb +0 -212
- data/lib/ransack/adapters/active_record/3.2/context.rb +0 -44
- data/lib/ransack/adapters/active_record/compat.rb +0 -14
- data/lib/ransack/adapters/mongoid/3.2/.gitkeep +0 -0
- data/lib/ransack/adapters/mongoid/attributes/attribute.rb +0 -37
- data/lib/ransack/adapters/mongoid/attributes/order_predications.rb +0 -17
- data/lib/ransack/adapters/mongoid/attributes/predications.rb +0 -141
- data/lib/ransack/adapters/mongoid/base.rb +0 -134
- data/lib/ransack/adapters/mongoid/context.rb +0 -212
- data/lib/ransack/adapters/mongoid/inquiry_hash.rb +0 -23
- data/lib/ransack/adapters/mongoid/ransack/constants.rb +0 -88
- data/lib/ransack/adapters/mongoid/ransack/context.rb +0 -60
- data/lib/ransack/adapters/mongoid/ransack/nodes/condition.rb +0 -27
- data/lib/ransack/adapters/mongoid/ransack/translate.rb +0 -13
- data/lib/ransack/adapters/mongoid/ransack/visitor.rb +0 -24
- data/lib/ransack/adapters/mongoid/table.rb +0 -35
- data/lib/ransack/adapters/mongoid.rb +0 -15
- data/spec/mongoid/adapters/mongoid/base_spec.rb +0 -314
- data/spec/mongoid/adapters/mongoid/context_spec.rb +0 -56
- data/spec/mongoid/configuration_spec.rb +0 -162
- data/spec/mongoid/dependencies_spec.rb +0 -8
- data/spec/mongoid/helpers/ransack_helper.rb +0 -11
- data/spec/mongoid/nodes/condition_spec.rb +0 -49
- data/spec/mongoid/nodes/grouping_spec.rb +0 -13
- data/spec/mongoid/predicate_spec.rb +0 -155
- data/spec/mongoid/search_spec.rb +0 -445
- data/spec/mongoid/support/mongoid.yml +0 -11
- data/spec/mongoid/support/schema.rb +0 -135
- data/spec/mongoid/translate_spec.rb +0 -14
- data/spec/mongoid_spec_helper.rb +0 -63
- 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,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,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,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,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
|