From 1e5c7d6f0fefd09e9c01f5b9d02aa2a071bf9695 Mon Sep 17 00:00:00 2001 From: evazion Date: Sun, 29 Aug 2021 22:37:16 -0500 Subject: [PATCH] Fix #4867: random=true in api only returns one post. Pundit 2.1.1 changed it so that if the first argument to `authorize` is an Array, then the `authorize` call returns the last element of the array. This broke order:random, because in that case we returned an Array of posts. The fix is to return an ActiveRecord::Relation of posts, which is more correct anyway. --- app/logical/post_sets/post.rb | 2 +- app/models/post.rb | 9 ++++++++- test/functional/posts_controller_test.rb | 9 +++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/logical/post_sets/post.rb b/app/logical/post_sets/post.rb index 7ecd2acfa..cca5a44fe 100644 --- a/app/logical/post_sets/post.rb +++ b/app/logical/post_sets/post.rb @@ -115,7 +115,7 @@ module PostSets @post_count = get_post_count if is_random? - get_random_posts + get_random_posts.paginate(page, search_count: false, limit: per_page, max_limit: max_per_page).load else normalized_query.build.paginate(page, count: post_count, search_count: !post_count.nil?, limit: per_page, max_limit: max_per_page).load end diff --git a/app/models/post.rb b/app/models/post.rb index 94f8a7c0f..f3fbe1d98 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -1145,11 +1145,18 @@ class Post < ApplicationRecord end module SearchMethods + # Return a set of up to N random posts. May return less if there aren't + # enough posts. + # + # @param n [Integer] The maximum number of posts to return + # @return [ActiveRecord::Relation] def random(n = 1) - n.times.map do + posts = n.times.map do key = SecureRandom.hex(16) random_up(key) || random_down(key) end.compact.uniq + + find_ordered(posts.map(&:id)) end def random_up(key) diff --git a/test/functional/posts_controller_test.rb b/test/functional/posts_controller_test.rb index bc97bc278..aa2f7face 100644 --- a/test/functional/posts_controller_test.rb +++ b/test/functional/posts_controller_test.rb @@ -322,6 +322,15 @@ class PostsControllerTest < ActionDispatch::IntegrationTest get posts_path, params: { random: "1" } assert_response :success end + + should "return all posts for a .json response" do + create_list(:post, 2, tag_string: "honk_honk") + get posts_path, params: { tags: "honk_honk order:random" }, as: :json + + assert_response :success + assert_equal(true, response.parsed_body.is_a?(Array)) + assert_equal(2, response.parsed_body.size) + end end context "with the .atom format" do