Skip to content

Commit bef286f

Browse files
committed
Add tiebreaker by primary key to ensure stable sorting
1 parent dd3ce76 commit bef286f

File tree

2 files changed

+58
-10
lines changed

2 files changed

+58
-10
lines changed

lib/administrate/order.rb

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,18 @@ def apply(relation)
1313

1414
order = relation.arel_table[sorting_column].public_send(direction)
1515

16-
return relation.reorder(order) if
17-
column_exist?(relation, sorting_column)
18-
19-
relation
16+
tiebreak_key = relation.primary_key
17+
tiebreak_order = relation.arel_table[tiebreak_key].public_send(direction)
18+
19+
if column_exist?(relation, sorting_column)
20+
if column_exist?(relation, tiebreak_key) && sorting_column.to_s != tiebreak_key.to_s
21+
relation.reorder(order, tiebreak_order)
22+
else
23+
relation.reorder(order)
24+
end
25+
else
26+
relation
27+
end
2028
end
2129

2230
def ordered_by?(attr)

spec/lib/administrate/order_spec.rb

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,16 @@
3030
end
3131

3232
context "when `order` argument is valid" do
33-
it "orders by the column" do
33+
it "orders by the column and tiebreaks by the primary key" do
3434
order = Administrate::Order.new(:name, :asc)
3535
relation = relation_with_column(:name)
3636
allow(relation).to receive(:reorder).and_return(relation)
3737

3838
ordered = order.apply(relation)
3939

4040
expect(relation).to have_received(:reorder).with(
41-
to_sql('"table_name"."name" ASC')
41+
to_sql('"table_name"."name" ASC'),
42+
to_sql('"table_name"."id" ASC')
4243
)
4344
expect(ordered).to eq(relation)
4445
end
@@ -51,7 +52,8 @@
5152
ordered = order.apply(relation)
5253

5354
expect(relation).to have_received(:reorder).with(
54-
to_sql('"table_name"."name" DESC')
55+
to_sql('"table_name"."name" DESC'),
56+
to_sql('"table_name"."id" DESC')
5557
)
5658
expect(ordered).to eq(relation)
5759
end
@@ -64,10 +66,47 @@
6466
ordered = order.apply(relation)
6567

6668
expect(relation).to have_received(:reorder).with(
67-
to_sql('"table_name"."name" ASC')
69+
to_sql('"table_name"."name" ASC'),
70+
to_sql('"table_name"."id" ASC')
6871
)
6972
expect(ordered).to eq(relation)
7073
end
74+
75+
context "and same with own primary key" do
76+
it "orders by the primary key" do
77+
order = Administrate::Order.new(:id, :asc)
78+
relation = relation_with_column(:name)
79+
allow(relation).to receive(:reorder).and_return(relation)
80+
81+
ordered = order.apply(relation)
82+
83+
expect(relation).to have_received(:reorder).with(
84+
to_sql('"table_name"."id" ASC')
85+
)
86+
expect(ordered).to eq(relation)
87+
end
88+
end
89+
90+
context "when the relation has no primary key" do
91+
it "orders by the column without tiebreaks" do
92+
order = Administrate::Order.new(:name, :asc)
93+
relation = double(
94+
klass: double(reflect_on_association: nil),
95+
columns_hash: {"name" => :column_info},
96+
table_name: "table_name",
97+
arel_table: Arel::Table.new("table_name"),
98+
primary_key: nil
99+
)
100+
allow(relation).to receive(:reorder).and_return(relation)
101+
102+
ordered = order.apply(relation)
103+
104+
expect(relation).to have_received(:reorder).with(
105+
to_sql('"table_name"."name" ASC')
106+
)
107+
expect(ordered).to eq(relation)
108+
end
109+
end
71110
end
72111

73112
context "when relation has_many association" do
@@ -329,9 +368,10 @@
329368
def relation_with_column(column)
330369
double(
331370
klass: double(reflect_on_association: nil),
332-
columns_hash: {column.to_s => :column_info},
371+
columns_hash: {column.to_s => :column_info, "id" => :column_info},
333372
table_name: "table_name",
334-
arel_table: Arel::Table.new("table_name")
373+
arel_table: Arel::Table.new("table_name"),
374+
primary_key: "id"
335375
)
336376
end
337377

0 commit comments

Comments
 (0)