DEV: Replace #pluck_first freedom patch with AR #pick in core (#19893)

The #pluck_first freedom patch, first introduced by @danielwaterworth has served us well, and is used widely throughout both core and plugins. It seems to have been a common enough use case that Rails 6 introduced it's own method #pick with the exact same implementation. This allows us to retire the freedom patch and switch over to the built-in ActiveRecord method.

There is no replacement for #pluck_first!, but a quick search shows we are using this in a very limited capacity, and in some cases incorrectly (by assuming a nil return rather than an exception), which can quite easily be replaced with #pick plus some extra handling.
This commit is contained in:
Ted Johansson 2023-02-13 12:39:45 +08:00 committed by GitHub
parent a90ad52dff
commit 25a226279a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 112 additions and 123 deletions

View File

@ -365,7 +365,7 @@ class CategoriesController < ApplicationController
if SiteSetting.enable_category_group_moderation? if SiteSetting.enable_category_group_moderation?
params[:reviewable_by_group_id] = Group.where( params[:reviewable_by_group_id] = Group.where(
name: params[:reviewable_by_group_name], name: params[:reviewable_by_group_name],
).pluck_first(:id) if params[:reviewable_by_group_name] ).pick(:id) if params[:reviewable_by_group_name]
end end
result = result =

View File

@ -70,7 +70,7 @@ class DirectoryItemsController < ApplicationController
end end
if params[:username] if params[:username]
user_id = User.where(username_lower: params[:username].to_s.downcase).pluck_first(:id) user_id = User.where(username_lower: params[:username].to_s.downcase).pick(:id)
if user_id if user_id
result = result.where(user_id: user_id) result = result.where(user_id: user_id)
else else

View File

@ -456,7 +456,7 @@ class ListController < ApplicationController
def self.best_period_for(previous_visit_at, category_id = nil) def self.best_period_for(previous_visit_at, category_id = nil)
default_period = default_period =
( (
(category_id && Category.where(id: category_id).pluck_first(:default_top_period)) || (category_id && Category.where(id: category_id).pick(:default_top_period)) ||
SiteSetting.top_page_default_timeframe SiteSetting.top_page_default_timeframe
).to_sym ).to_sym

View File

@ -615,7 +615,7 @@ class PostsController < ApplicationController
bookmarkable_id: params[:post_id], bookmarkable_id: params[:post_id],
bookmarkable_type: "Post", bookmarkable_type: "Post",
user_id: current_user.id, user_id: current_user.id,
).pluck_first(:id) ).pick(:id)
destroyed_bookmark = BookmarkManager.new(current_user).destroy(bookmark_id) destroyed_bookmark = BookmarkManager.new(current_user).destroy(bookmark_id)
render json: render json:

View File

@ -59,7 +59,7 @@ class StylesheetsController < ApplicationController
cache_path = Stylesheet::Manager.cache_fullpath cache_path = Stylesheet::Manager.cache_fullpath
location = "#{cache_path}/#{target}#{underscore_digest}#{extension}" location = "#{cache_path}/#{target}#{underscore_digest}#{extension}"
stylesheet_time = query.pluck_first(:created_at) stylesheet_time = query.pick(:created_at)
handle_missing_cache(location, target, digest) if !stylesheet_time handle_missing_cache(location, target, digest) if !stylesheet_time
@ -68,7 +68,7 @@ class StylesheetsController < ApplicationController
end end
unless File.exist?(location) unless File.exist?(location)
if current = query.pluck_first(source_map ? :source_map : :content) if current = query.pick(source_map ? :source_map : :content)
FileUtils.mkdir_p(cache_path) FileUtils.mkdir_p(cache_path)
File.write(location, current) File.write(location, current)
else else

View File

@ -22,7 +22,7 @@ class ThemeJavascriptsController < ApplicationController
cache_file = "#{DISK_CACHE_PATH}/#{params[:digest]}.js" cache_file = "#{DISK_CACHE_PATH}/#{params[:digest]}.js"
write_if_not_cached(cache_file) do write_if_not_cached(cache_file) do
content, has_source_map = query.pluck_first(:content, "source_map IS NOT NULL") content, has_source_map = query.pick(:content, "source_map IS NOT NULL")
if has_source_map if has_source_map
content += content +=
"\n//# sourceMappingURL=#{params[:digest]}.map?__ws=#{Discourse.current_hostname}\n" "\n//# sourceMappingURL=#{params[:digest]}.map?__ws=#{Discourse.current_hostname}\n"
@ -40,7 +40,7 @@ class ThemeJavascriptsController < ApplicationController
# Security: safe due to route constraint # Security: safe due to route constraint
cache_file = "#{DISK_CACHE_PATH}/#{params[:digest]}.map" cache_file = "#{DISK_CACHE_PATH}/#{params[:digest]}.map"
write_if_not_cached(cache_file) { query.pluck_first(:source_map) } write_if_not_cached(cache_file) { query.pick(:source_map) }
serve_file(cache_file) serve_file(cache_file)
end end
@ -75,7 +75,7 @@ class ThemeJavascriptsController < ApplicationController
if params[:action].to_s == "show_tests" if params[:action].to_s == "show_tests"
File.exist?(@cache_file) ? File.ctime(@cache_file) : nil File.exist?(@cache_file) ? File.ctime(@cache_file) : nil
else else
query.pluck_first(:updated_at) query.pick(:updated_at)
end end
end end
end end

View File

@ -843,7 +843,7 @@ class TopicsController < ApplicationController
if params[:title].present? if params[:title].present?
# when creating a new topic, ensure the 1st post is a regular post # when creating a new topic, ensure the 1st post is a regular post
if Post.where(topic: topic, id: post_ids).order(:post_number).pluck_first(:post_type) != if Post.where(topic: topic, id: post_ids).order(:post_number).pick(:post_type) !=
Post.types[:regular] Post.types[:regular]
return( return(
render_json_error( render_json_error(

View File

@ -22,7 +22,7 @@ class UserBadgesController < ApplicationController
grant_count = nil grant_count = nil
if params[:username] if params[:username]
user_id = User.where(username_lower: params[:username].downcase).pluck_first(:id) user_id = User.where(username_lower: params[:username].downcase).pick(:id)
user_badges = user_badges.where(user_id: user_id) if user_id user_badges = user_badges.where(user_id: user_id) if user_id
grant_count = badge.user_badges.where(user_id: user_id).count grant_count = badge.user_badges.where(user_id: user_id).count
end end

View File

@ -190,7 +190,7 @@ module ApplicationHelper
result << "category-#{@category.slug_path.join("-")}" if @category && @category.url.present? result << "category-#{@category.slug_path.join("-")}" if @category && @category.url.present?
if current_user.present? && current_user.primary_group_id && if current_user.present? && current_user.primary_group_id &&
primary_group_name = Group.where(id: current_user.primary_group_id).pluck_first(:name) primary_group_name = Group.where(id: current_user.primary_group_id).pick(:name)
result << "primary-group-#{primary_group_name.downcase}" result << "primary-group-#{primary_group_name.downcase}"
end end
@ -544,7 +544,7 @@ module ApplicationHelper
return if theme_id.blank? return if theme_id.blank?
@scheme_id = Theme.where(id: theme_id).pluck_first(:color_scheme_id) @scheme_id = Theme.where(id: theme_id).pick(:color_scheme_id)
end end
def dark_scheme_id def dark_scheme_id

View File

@ -197,9 +197,11 @@ module Jobs
end end
def quotes_correct_user?(aside) def quotes_correct_user?(aside)
Post.where(topic_id: aside["data-topic"], post_number: aside["data-post"]).pluck_first( Post.exists?(
:user_id, topic_id: aside["data-topic"],
) == @user_id post_number: aside["data-post"],
user_id: @user_id,
)
end end
end end
end end

View File

@ -504,7 +504,7 @@ class UserNotifications < ActionMailer::Base
user_name = notification_data[:original_username] user_name = notification_data[:original_username]
if post && SiteSetting.enable_names && SiteSetting.display_name_on_email_from if post && SiteSetting.enable_names && SiteSetting.display_name_on_email_from
name = User.where(id: notification_data[:original_user_id] || post.user_id).pluck_first(:name) name = User.where(id: notification_data[:original_user_id] || post.user_id).pick(:name)
user_name = name unless name.blank? user_name = name unless name.blank?
end end
@ -589,7 +589,7 @@ class UserNotifications < ActionMailer::Base
# subcategory case # subcategory case
if !category.parent_category_id.nil? if !category.parent_category_id.nil?
show_category_in_subject = show_category_in_subject =
"#{Category.where(id: category.parent_category_id).pluck_first(:name)}/#{show_category_in_subject}" "#{Category.where(id: category.parent_category_id).pick(:name)}/#{show_category_in_subject}"
end end
else else
show_category_in_subject = nil show_category_in_subject = nil

View File

@ -39,9 +39,7 @@ class ApplicationRequest < ActiveRecord::Base
def self.req_id(date, req_type, retries = 0) def self.req_id(date, req_type, retries = 0)
req_type_id = req_types[req_type] req_type_id = req_types[req_type]
# a poor man's upsert create_or_find_by!(date: date, req_type: req_type_id).id
id = where(date: date, req_type: req_type_id).pluck_first(:id)
id ||= create!(date: date, req_type: req_type_id, count: 0).id
rescue StandardError # primary key violation rescue StandardError # primary key violation
if retries == 0 if retries == 0
req_id(date, req_type, 1) req_id(date, req_type, 1)

View File

@ -4,7 +4,7 @@ class BackupMetadata < ActiveRecord::Base
LAST_RESTORE_DATE = "last_restore_date" LAST_RESTORE_DATE = "last_restore_date"
def self.value_for(name) def self.value_for(name)
where(name: name).pluck_first(:value).presence where(name: name).pick(:value).presence
end end
def self.last_restore_date def self.last_restore_date

View File

@ -790,9 +790,8 @@ class Category < ActiveRecord::Base
def self.query_parent_category(parent_slug) def self.query_parent_category(parent_slug)
encoded_parent_slug = CGI.escape(parent_slug) if SiteSetting.slug_generation_method == "encoded" encoded_parent_slug = CGI.escape(parent_slug) if SiteSetting.slug_generation_method == "encoded"
self.where(slug: (encoded_parent_slug || parent_slug), parent_category_id: nil).pluck_first( self.where(slug: (encoded_parent_slug || parent_slug), parent_category_id: nil).pick(:id) ||
:id, self.where(id: parent_slug.to_i).pick(:id)
) || self.where(id: parent_slug.to_i).pluck_first(:id)
end end
def self.query_category(slug_or_id, parent_category_id) def self.query_category(slug_or_id, parent_category_id)

View File

@ -252,7 +252,7 @@ class Draft < ActiveRecord::Base
reply = JSON.parse(data)["reply"] || "" reply = JSON.parse(data)["reply"] || ""
return if reply.length < SiteSetting.backup_drafts_to_pm_length return if reply.length < SiteSetting.backup_drafts_to_pm_length
post_id = BackupDraftPost.where(user_id: user.id, key: key).pluck_first(:post_id) post_id = BackupDraftPost.where(user_id: user.id, key: key).pick(:post_id)
post = Post.where(id: post_id).first if post_id post = Post.where(id: post_id).first if post_id
BackupDraftPost.where(user_id: user.id, key: key).delete_all if post_id && !post BackupDraftPost.where(user_id: user.id, key: key).delete_all if post_id && !post
@ -305,7 +305,7 @@ class Draft < ActiveRecord::Base
end end
def self.ensure_draft_topic!(user) def self.ensure_draft_topic!(user)
topic_id = BackupDraftTopic.where(user_id: user.id).pluck_first(:topic_id) topic_id = BackupDraftTopic.where(user_id: user.id).pick(:topic_id)
topic = Topic.find_by(id: topic_id) if topic_id topic = Topic.find_by(id: topic_id) if topic_id
BackupDraftTopic.where(user_id: user.id).delete_all if topic_id && !topic BackupDraftTopic.where(user_id: user.id).delete_all if topic_id && !topic

View File

@ -37,7 +37,7 @@ class IncomingLink < ActiveRecord::Base
if host != opts[:host] && (user_id || referer) if host != opts[:host] && (user_id || referer)
post_id = opts[:post_id] post_id = opts[:post_id]
post_id ||= post_id ||=
Post.where(topic_id: opts[:topic_id], post_number: opts[:post_number] || 1).pluck_first(:id) Post.where(topic_id: opts[:topic_id], post_number: opts[:post_number] || 1).pick(:id)
cid = current_user ? (current_user.id) : (nil) cid = current_user ? (current_user.id) : (nil)
ip_address = nil if cid ip_address = nil if cid

View File

@ -348,7 +348,7 @@ class Notification < ActiveRecord::Base
end end
def post_id def post_id
Post.where(topic: topic_id, post_number: post_number).pluck_first(:id) Post.where(topic: topic_id, post_number: post_number).pick(:id)
end end
protected protected

View File

@ -1149,7 +1149,7 @@ class Post < ActiveRecord::Base
end end
upload_id = nil upload_id = nil
upload_id = Upload.where(sha1: sha1).pluck_first(:id) if sha1.present? upload_id = Upload.where(sha1: sha1).pick(:id) if sha1.present?
upload_id ||= yield(post, src, path, sha1) upload_id ||= yield(post, src, path, sha1)
if upload_id.blank? if upload_id.blank?

View File

@ -242,7 +242,7 @@ class PostAction < ActiveRecord::Base
end end
end end
topic_id = Post.with_deleted.where(id: post_id).pluck_first(:topic_id) topic_id = Post.with_deleted.where(id: post_id).pick(:topic_id)
# topic_user # topic_user
if post_action_type_key == :like if post_action_type_key == :like

View File

@ -68,7 +68,7 @@ class QuotedPost < ActiveRecord::Base
if post.reply_to_post_number if post.reply_to_post_number
reply_post_id = reply_post_id =
Post.where(topic_id: post.topic_id, post_number: post.reply_to_post_number).pluck_first(:id) Post.where(topic_id: post.topic_id, post_number: post.reply_to_post_number).pick(:id)
reply_quoted = reply_quoted =
reply_post_id.present? && reply_post_id.present? &&
QuotedPost.where(post_id: post.id, quoted_post_id: reply_post_id).count > 0 QuotedPost.where(post_id: post.id, quoted_post_id: reply_post_id).count > 0

View File

@ -147,7 +147,7 @@ class ScreenedIpAddress < ActiveRecord::Base
.where("masklen(ip_address) IN (?)", from_masklen) .where("masklen(ip_address) IN (?)", from_masklen)
sum_match_count, max_last_match_at, min_created_at = sum_match_count, max_last_match_at, min_created_at =
old_ips.pluck_first("SUM(match_count), MAX(last_match_at), MIN(created_at)") old_ips.pick("SUM(match_count), MAX(last_match_at), MIN(created_at)")
ScreenedIpAddress.create!( ScreenedIpAddress.create!(
ip_address: subnet, ip_address: subnet,

View File

@ -893,7 +893,7 @@ class Topic < ActiveRecord::Base
# If a post is deleted we have to update our highest post counters and last post information # If a post is deleted we have to update our highest post counters and last post information
def self.reset_highest(topic_id) def self.reset_highest(topic_id)
archetype = Topic.where(id: topic_id).pluck_first(:archetype) archetype = Topic.where(id: topic_id).pick(:archetype)
# ignore small_action replies for private messages # ignore small_action replies for private messages
post_type = post_type =

View File

@ -20,7 +20,7 @@ class TopicConverter
.where(read_restricted: false) .where(read_restricted: false)
.where.not(id: SiteSetting.uncategorized_category_id) .where.not(id: SiteSetting.uncategorized_category_id)
.order("id asc") .order("id asc")
.pluck_first(:id) .pick(:id)
end end
PostRevisor.new(@topic.first_post, @topic).revise!( PostRevisor.new(@topic.first_post, @topic).revise!(

View File

@ -254,9 +254,7 @@ class TopicEmbed < ActiveRecord::Base
def self.topic_id_for_embed(embed_url) def self.topic_id_for_embed(embed_url)
embed_url = normalize_url(embed_url).sub(%r{\Ahttps?\://}, "") embed_url = normalize_url(embed_url).sub(%r{\Ahttps?\://}, "")
TopicEmbed.where("embed_url ~* ?", "^https?://#{Regexp.escape(embed_url)}$").pluck_first( TopicEmbed.where("embed_url ~* ?", "^https?://#{Regexp.escape(embed_url)}$").pick(:topic_id)
:topic_id,
)
end end
def self.first_paragraph_from(html) def self.first_paragraph_from(html)
@ -281,7 +279,7 @@ class TopicEmbed < ActiveRecord::Base
Discourse Discourse
.cache .cache
.fetch("embed-topic:#{post.topic_id}", expires_in: 10.minutes) do .fetch("embed-topic:#{post.topic_id}", expires_in: 10.minutes) do
url = TopicEmbed.where(topic_id: post.topic_id).pluck_first(:embed_url) url = TopicEmbed.where(topic_id: post.topic_id).pick(:embed_url)
response = TopicEmbed.find_remote(url) response = TopicEmbed.find_remote(url)
body = response.body body = response.body

View File

@ -275,8 +275,7 @@ class TopicUser < ActiveRecord::Base
attrs[:notification_level] = notification_levels[:watching] attrs[:notification_level] = notification_levels[:watching]
end end
else else
auto_track_after = auto_track_after = UserOption.where(user_id: user_id).pick(:auto_track_topics_after_msecs)
UserOption.where(user_id: user_id).pluck_first(:auto_track_topics_after_msecs)
auto_track_after ||= SiteSetting.default_other_auto_track_topics_after_msecs auto_track_after ||= SiteSetting.default_other_auto_track_topics_after_msecs
if auto_track_after >= 0 && auto_track_after <= (attrs[:total_msecs_viewed].to_i || 0) if auto_track_after >= 0 && auto_track_after <= (attrs[:total_msecs_viewed].to_i || 0)

View File

@ -294,7 +294,7 @@ class User < ActiveRecord::Base
->(filter) { ->(filter) {
if filter.is_a?(String) && filter =~ /.+@.+/ if filter.is_a?(String) && filter =~ /.+@.+/
# probably an email so try the bypass # probably an email so try the bypass
if user_id = UserEmail.where("lower(email) = ?", filter.downcase).pluck_first(:user_id) if user_id = UserEmail.where("lower(email) = ?", filter.downcase).pick(:user_id)
return where("users.id = ?", user_id) return where("users.id = ?", user_id)
end end
end end
@ -1683,11 +1683,11 @@ class User < ActiveRecord::Base
group_titles_query.order("groups.id = #{primary_group_id} DESC") if primary_group_id group_titles_query.order("groups.id = #{primary_group_id} DESC") if primary_group_id
group_titles_query = group_titles_query.order("groups.primary_group DESC").limit(1) group_titles_query = group_titles_query.order("groups.primary_group DESC").limit(1)
if next_best_group_title = group_titles_query.pluck_first(:title) if next_best_group_title = group_titles_query.pick(:title)
return next_best_group_title return next_best_group_title
end end
next_best_badge_title = badges.where(allow_title: true).pluck_first(:name) next_best_badge_title = badges.where(allow_title: true).pick(:name)
next_best_badge_title ? Badge.display_name(next_best_badge_title) : nil next_best_badge_title ? Badge.display_name(next_best_badge_title) : nil
end end
@ -2030,9 +2030,7 @@ class User < ActiveRecord::Base
def match_primary_group_changes def match_primary_group_changes
return unless primary_group_id_changed? return unless primary_group_id_changed?
if title == Group.where(id: primary_group_id_was).pluck_first(:title) self.title = primary_group&.title if Group.exists?(id: primary_group_id_was, title: title)
self.title = primary_group&.title
end
self.flair_group_id = primary_group&.id if flair_group_id == primary_group_id_was self.flair_group_id = primary_group&.id if flair_group_id == primary_group_id_was
end end
@ -2043,7 +2041,7 @@ class User < ActiveRecord::Base
.human_users .human_users
.joins(:user_auth_tokens) .joins(:user_auth_tokens)
.order("user_auth_tokens.created_at") .order("user_auth_tokens.created_at")
.pluck_first(:id) .pick(:id)
end end
private private

View File

@ -69,7 +69,7 @@ class UserAction < ActiveRecord::Base
UserAction UserAction
.where(user_id: user_id, target_topic_id: topic_id, action_type: [RESPONSE, MENTION, QUOTE]) .where(user_id: user_id, target_topic_id: topic_id, action_type: [RESPONSE, MENTION, QUOTE])
.order("created_at DESC") .order("created_at DESC")
.pluck_first(:target_post_id) .pick(:target_post_id)
end end
def self.stats(user_id, guardian) def self.stats(user_id, guardian)

View File

@ -24,7 +24,7 @@ class WebCrawlerRequest < ActiveRecord::Base
protected protected
def self.request_id(date:, user_agent:, retries: 0) def self.request_id(date:, user_agent:, retries: 0)
id = where(date: date, user_agent: user_agent).pluck_first(:id) id = where(date: date, user_agent: user_agent).pick(:id)
id ||= create!({ date: date, user_agent: user_agent }.merge(count: 0)).id id ||= create!({ date: date, user_agent: user_agent }.merge(count: 0)).id
rescue StandardError # primary key violation rescue StandardError # primary key violation
if retries == 0 if retries == 0

View File

@ -33,7 +33,7 @@ module SuggestedTopicsMixin
object.topic_allowed_group_ids, object.topic_allowed_group_ids,
scope.user.id, scope.user.id,
) )
.pluck_first(:name) .pick(:name)
end end
end end

View File

@ -283,7 +283,7 @@ class TopicViewSerializer < ApplicationSerializer
owner: true, owner: true,
}, },
) )
.pluck_first(:name) .pick(:name)
end end
def include_requested_group_name? def include_requested_group_name?

View File

@ -385,8 +385,7 @@ class PostAlerter
stats = (@group_stats[topic.id] ||= group_stats(topic)) stats = (@group_stats[topic.id] ||= group_stats(topic))
return unless stats return unless stats
group_id = group_id = topic.topic_allowed_groups.where(group_id: user.groups.pluck(:id)).pick(:group_id)
topic.topic_allowed_groups.where(group_id: user.groups.pluck(:id)).pluck_first(:group_id)
stat = stats.find { |s| s[:group_id] == group_id } stat = stats.find { |s| s[:group_id] == group_id }
return unless stat return unless stat

View File

@ -30,7 +30,7 @@ if !Theme.exists?
if SiteSetting.default_dark_mode_color_scheme_id == if SiteSetting.default_dark_mode_color_scheme_id ==
SiteSetting.defaults[:default_dark_mode_color_scheme_id] SiteSetting.defaults[:default_dark_mode_color_scheme_id]
dark_scheme_id = ColorScheme.where(base_scheme_id: "Dark").pluck_first(:id) dark_scheme_id = ColorScheme.where(base_scheme_id: "Dark").pick(:id)
SiteSetting.default_dark_mode_color_scheme_id = dark_scheme_id if dark_scheme_id.present? SiteSetting.default_dark_mode_color_scheme_id = dark_scheme_id if dark_scheme_id.present?
end end

View File

@ -206,7 +206,7 @@ class ComposerMessagesFinder
topic_id: @details[:topic_id], topic_id: @details[:topic_id],
) )
reply_username = User.where(id: last_x_replies[0]).pluck_first(:username) reply_username = User.where(id: last_x_replies[0]).pick(:username)
{ {
id: "get_a_room", id: "get_a_room",

View File

@ -122,7 +122,7 @@ module Email
if from_address.blank? if from_address.blank?
nil nil
else else
Group.where(email_username: from_address, smtp_enabled: true).pluck_first(:id) Group.where(email_username: from_address, smtp_enabled: true).pick(:id)
end end
) )

View File

@ -409,7 +409,7 @@ module FileStore
verified_ids = [] verified_ids = []
files.each do |f| files.each do |f|
id = model.where("url LIKE '%#{f.key}' AND etag = '#{f.etag}'").pluck_first(:id) id = model.where("url LIKE '%#{f.key}' AND etag = '#{f.etag}'").pick(:id)
verified_ids << id if id.present? verified_ids << id if id.present?
marker = f.key marker = f.key
end end

View File

@ -2,15 +2,17 @@
class ActiveRecord::Relation class ActiveRecord::Relation
def pluck_first(*attributes) def pluck_first(*attributes)
limit(1).pluck(*attributes).first Discourse.deprecate("`#pluck_first` is deprecated, use `#pick` instead.")
pick(*attributes)
end end
def pluck_first!(*attributes) def pluck_first!(*attributes)
items = limit(1).pluck(*attributes) Discourse.deprecate("`#pluck_first!` is deprecated without replacement.")
items = pick(*attributes)
raise_record_not_found_exception! if items.empty? raise_record_not_found_exception! if items.nil?
items.first items
end end
end end

View File

@ -368,8 +368,7 @@ class Guardian
def can_use_flair_group?(user, group_id = nil) def can_use_flair_group?(user, group_id = nil)
return false if !user || !group_id || !user.group_ids.include?(group_id.to_i) return false if !user || !group_id || !user.group_ids.include?(group_id.to_i)
flair_icon, flair_upload_id = flair_icon, flair_upload_id = Group.where(id: group_id.to_i).pick(:flair_icon, :flair_upload_id)
Group.where(id: group_id.to_i).pluck_first(:flair_icon, :flair_upload_id)
flair_icon.present? || flair_upload_id.present? flair_icon.present? || flair_upload_id.present?
end end

View File

@ -328,9 +328,8 @@ class PostCreator
if PostCreator.track_post_stats if PostCreator.track_post_stats
sequence = DraftSequence.current(@user, draft_key) sequence = DraftSequence.current(@user, draft_key)
revisions = revisions =
Draft.where(sequence: sequence, user_id: @user.id, draft_key: draft_key).pluck_first( Draft.where(sequence: sequence, user_id: @user.id, draft_key: draft_key).pick(:revisions) ||
:revisions, 0
) || 0
@post.build_post_stat( @post.build_post_stat(
drafts_saved: revisions, drafts_saved: revisions,

View File

@ -552,8 +552,7 @@ class PostRevisor
if revision.modifications.empty? if revision.modifications.empty?
revision.destroy revision.destroy
@post.last_editor_id = @post.last_editor_id =
PostRevision.where(post_id: @post.id).order(number: :desc).pluck_first(:user_id) || PostRevision.where(post_id: @post.id).order(number: :desc).pick(:user_id) || @post.user_id
@post.user_id
@post.version -= 1 @post.version -= 1
@post.public_version -= 1 @post.public_version -= 1
@post.save(validate: @validate_post) @post.save(validate: @validate_post)

View File

@ -430,7 +430,7 @@ class Search
advanced_filter(/\Ain:wiki\z/i) { |posts, match| posts.where(wiki: true) } advanced_filter(/\Ain:wiki\z/i) { |posts, match| posts.where(wiki: true) }
advanced_filter(/\Abadge:(.*)\z/i) do |posts, match| advanced_filter(/\Abadge:(.*)\z/i) do |posts, match|
badge_id = Badge.where("name ilike ? OR id = ?", match, match.to_i).pluck_first(:id) badge_id = Badge.where("name ilike ? OR id = ?", match, match.to_i).pick(:id)
if badge_id if badge_id
posts.where( posts.where(
"posts.user_id IN (SELECT ub.user_id FROM user_badges ub WHERE ub.badge_id = ?)", "posts.user_id IN (SELECT ub.user_id FROM user_badges ub WHERE ub.badge_id = ?)",
@ -480,7 +480,7 @@ class Search
end end
advanced_filter(/\Acreated:@(.*)\z/i) do |posts, match| advanced_filter(/\Acreated:@(.*)\z/i) do |posts, match|
user_id = User.where(username: match.downcase).pluck_first(:id) user_id = User.where(username: match.downcase).pick(:id)
posts.where(user_id: user_id, post_number: 1) posts.where(user_id: user_id, post_number: 1)
end end
@ -563,12 +563,12 @@ class Search
parent_category_id: parent_category_id:
Category.where("lower(slug) = ?", category_slug.downcase).select(:id), Category.where("lower(slug) = ?", category_slug.downcase).select(:id),
) )
.pluck_first(:id) .pick(:id)
else else
Category Category
.where("lower(slug) = ?", category_slug.downcase) .where("lower(slug) = ?", category_slug.downcase)
.order("case when parent_category_id is null then 0 else 1 end") .order("case when parent_category_id is null then 0 else 1 end")
.pluck_first(:id) .pick(:id)
end end
if category_id if category_id
@ -579,7 +579,7 @@ class Search
posts.where("topics.category_id IN (?)", category_ids) posts.where("topics.category_id IN (?)", category_ids)
else else
# try a possible tag match # try a possible tag match
tag_id = Tag.where_name(category_slug).pluck_first(:id) tag_id = Tag.where_name(category_slug).pick(:id)
if (tag_id) if (tag_id)
posts.where(<<~SQL, tag_id) posts.where(<<~SQL, tag_id)
topics.id IN ( topics.id IN (
@ -625,7 +625,7 @@ class Search
group_query = cb.call(group_query, @term, @guardian) group_query = cb.call(group_query, @term, @guardian)
end end
group_id = group_query.pluck_first(:id) group_id = group_query.pick(:id)
if group_id if group_id
posts.where( posts.where(
@ -644,7 +644,7 @@ class Search
.members_visible_groups(@guardian.user) .members_visible_groups(@guardian.user)
.where(has_messages: true) .where(has_messages: true)
.where("name ilike ? OR (id = ? AND id > 0)", match, match.to_i) .where("name ilike ? OR (id = ? AND id > 0)", match, match.to_i)
.pluck_first(:id) .pick(:id)
if group_id if group_id
posts.where( posts.where(
@ -661,7 +661,7 @@ class Search
User User
.where(staged: false) .where(staged: false)
.where("username_lower = ? OR id = ?", match.downcase, match.to_i) .where("username_lower = ? OR id = ?", match.downcase, match.to_i)
.pluck_first(:id) .pick(:id)
if user_id if user_id
posts.where("posts.user_id = ?", user_id) posts.where("posts.user_id = ?", user_id)
else else
@ -672,7 +672,7 @@ class Search
advanced_filter(/\A\@(\S+)\z/i) do |posts, match| advanced_filter(/\A\@(\S+)\z/i) do |posts, match|
username = User.normalize_username(match) username = User.normalize_username(match)
user_id = User.not_staged.where(username_lower: username).pluck_first(:id) user_id = User.not_staged.where(username_lower: username).pick(:id)
user_id = @guardian.user&.id if !user_id && username == "me" user_id = @guardian.user&.id if !user_id && username == "me"

View File

@ -543,7 +543,7 @@ def recover_uploads_from_index(path)
.pluck(:post_id, :value) .pluck(:post_id, :value)
.each do |post_id, uploads| .each do |post_id, uploads|
uploads = JSON.parse(uploads) uploads = JSON.parse(uploads)
raw = Post.where(id: post_id).pluck_first(:raw) raw = Post.where(id: post_id).pick(:raw)
uploads.map! do |upload| uploads.map! do |upload|
orig = upload orig = upload
if raw.scan(upload).length == 0 if raw.scan(upload).length == 0

View File

@ -630,8 +630,7 @@ class TopicQuery
category_id = category_id_or_slug.to_i category_id = category_id_or_slug.to_i
if category_id == 0 if category_id == 0
category_id = category_id = Category.where(slug: category_id_or_slug, parent_category_id: nil).pick(:id)
Category.where(slug: category_id_or_slug, parent_category_id: nil).pluck_first(:id)
end end
category_id category_id
@ -678,7 +677,7 @@ class TopicQuery
filter = (options[:filter] || options[:f]) filter = (options[:filter] || options[:f])
# category default sort order # category default sort order
sort_order, sort_ascending = sort_order, sort_ascending =
Category.where(id: category_id).pluck_first(:sort_order, :sort_ascending) Category.where(id: category_id).pick(:sort_order, :sort_ascending)
if sort_order && (filter.blank? || %i[latest unseen].include?(filter)) if sort_order && (filter.blank? || %i[latest unseen].include?(filter))
options[:order] = sort_order options[:order] = sort_order
options[:ascending] = !!sort_ascending ? "true" : "false" options[:ascending] = !!sort_ascending ? "true" : "false"
@ -1023,7 +1022,7 @@ class TopicQuery
:first_unread_pm_at, :first_unread_pm_at,
) )
else else
UserStat.where(user_id: @user.id).pluck_first(:first_unread_pm_at) UserStat.where(user_id: @user.id).pick(:first_unread_pm_at)
end end
query = query.where("topics.updated_at >= ?", first_unread_pm_at) if first_unread_pm_at query = query.where("topics.updated_at >= ?", first_unread_pm_at) if first_unread_pm_at

View File

@ -188,7 +188,7 @@ class TopicQuery
when :user when :user
user_first_unread_pm_at(user) user_first_unread_pm_at(user)
when :group when :group
GroupUser.where(user: user, group: group).pluck_first(:first_unread_pm_at) GroupUser.where(user: user, group: group).pick(:first_unread_pm_at)
else else
user_first_unread_pm_at = user_first_unread_pm_at(user) user_first_unread_pm_at = user_first_unread_pm_at(user)
@ -284,7 +284,7 @@ class TopicQuery
end end
def user_first_unread_pm_at(user) def user_first_unread_pm_at(user)
UserStat.where(user: user).pluck_first(:first_unread_pm_at) UserStat.where(user: user).pick(:first_unread_pm_at)
end end
def group_with_messages_ids(user) def group_with_messages_ids(user)

View File

@ -671,7 +671,7 @@ class TopicView
end end
def filtered_post_id(post_number) def filtered_post_id(post_number)
@filtered_posts.where(post_number: post_number).pluck_first(:id) @filtered_posts.where(post_number: post_number).pick(:id)
end end
def is_mega_topic? def is_mega_topic?
@ -679,7 +679,7 @@ class TopicView
end end
def last_post_id def last_post_id
@filtered_posts.reverse_order.pluck_first(:id) @filtered_posts.reverse_order.pick(:id)
end end
def current_post_number def current_post_number

View File

@ -48,7 +48,7 @@ class TrustLevel
.where(action: UserHistory.actions[:change_trust_level]) .where(action: UserHistory.actions[:change_trust_level])
.where(target_user_id: user.id) .where(target_user_id: user.id)
.order(created_at: :desc) .order(created_at: :desc)
.pluck_first(:new_value) .pick(:new_value)
.to_i .to_i
end end
end end

View File

@ -13,7 +13,7 @@ class MaxUsernameLengthValidator
return false return false
end end
return false if value < SiteSetting.min_username_length return false if value < SiteSetting.min_username_length
@username = User.where("length(username) > ?", value).pluck_first(:username) @username = User.where("length(username) > ?", value).pick(:username)
@username.blank? @username.blank?
end end

View File

@ -13,7 +13,7 @@ class MinUsernameLengthValidator
return false return false
end end
return false if value > SiteSetting.max_username_length return false if value > SiteSetting.max_username_length
@username = User.where("length(username) < ?", value).pluck_first(:username) @username = User.where("length(username) < ?", value).pick(:username)
@username.blank? @username.blank?
end end

View File

@ -58,7 +58,7 @@ module ImportScripts
def find_username_by_import_id(import_id) def find_username_by_import_id(import_id)
user_id = user_id_from_imported_user_id(import_id) user_id = user_id_from_imported_user_id(import_id)
User.where(id: user_id).pluck_first(:username) if user_id.present? User.where(id: user_id).pick(:username) if user_id.present?
end end
# Get the Discourse Category id based on the id of the source category # Get the Discourse Category id based on the id of the source category

View File

@ -14,7 +14,7 @@ Benchmark.ips do |b|
b.report("pluck with limit") { User.limit(1).pluck(:name).first } b.report("pluck with limit") { User.limit(1).pluck(:name).first }
b.report("pluck with pluck_first") { User.pluck_first(:name) } b.report("pluck with pick") { User.pick(:name) }
b.report("raw") { conn.exec("SELECT name FROM users LIMIT 1").getvalue(0, 0) } b.report("raw") { conn.exec("SELECT name FROM users LIMIT 1").getvalue(0, 0) }
end end

View File

@ -683,8 +683,7 @@ RSpec.describe ApplicationHelper do
end end
it "returns two color scheme link tags when dark mode is enabled" do it "returns two color scheme link tags when dark mode is enabled" do
SiteSetting.default_dark_mode_color_scheme_id = SiteSetting.default_dark_mode_color_scheme_id = ColorScheme.where(name: "Dark").pick(:id)
ColorScheme.where(name: "Dark").pluck_first(:id)
cs_stylesheets = helper.discourse_color_scheme_stylesheets cs_stylesheets = helper.discourse_color_scheme_stylesheets
expect(cs_stylesheets).to include("(prefers-color-scheme: dark)") expect(cs_stylesheets).to include("(prefers-color-scheme: dark)")
@ -740,8 +739,7 @@ RSpec.describe ApplicationHelper do
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
@new_cs = Fabricate(:color_scheme, name: "Custom Color Scheme") @new_cs = Fabricate(:color_scheme, name: "Custom Color Scheme")
SiteSetting.default_dark_mode_color_scheme_id = SiteSetting.default_dark_mode_color_scheme_id = ColorScheme.where(name: "Dark").pick(:id)
ColorScheme.where(name: "Dark").pluck_first(:id)
end end
it "returns no dark scheme stylesheet when user has disabled that option" do it "returns no dark scheme stylesheet when user has disabled that option" do

View File

@ -370,7 +370,7 @@ RSpec.describe Wizard::StepUpdater do
expect(SiteSetting.contact_email).to eq("eviltrout@example.com") expect(SiteSetting.contact_email).to eq("eviltrout@example.com")
# Should update the TOS topic # Should update the TOS topic
raw = Post.where(topic_id: SiteSetting.tos_topic_id, post_number: 1).pluck_first(:raw) raw = Post.where(topic_id: SiteSetting.tos_topic_id, post_number: 1).pick(:raw)
expect(raw).to eq("ACME, Inc. - New Jersey law - Fairfield, New Jersey template") expect(raw).to eq("ACME, Inc. - New Jersey law - Fairfield, New Jersey template")
# Can update the TOS topic again # Can update the TOS topic again
@ -382,13 +382,13 @@ RSpec.describe Wizard::StepUpdater do
city_for_disputes: "San Francisco, California", city_for_disputes: "San Francisco, California",
) )
updater.update updater.update
raw = Post.where(topic_id: SiteSetting.tos_topic_id, post_number: 1).pluck_first(:raw) raw = Post.where(topic_id: SiteSetting.tos_topic_id, post_number: 1).pick(:raw)
expect(raw).to eq("Pied Piper Inc - California law - San Francisco, California template") expect(raw).to eq("Pied Piper Inc - California law - San Francisco, California template")
# Can update the TOS to nothing # Can update the TOS to nothing
updater = wizard.create_updater("corporate", {}) updater = wizard.create_updater("corporate", {})
updater.update updater.update
raw = Post.where(topic_id: SiteSetting.tos_topic_id, post_number: 1).pluck_first(:raw) raw = Post.where(topic_id: SiteSetting.tos_topic_id, post_number: 1).pick(:raw)
expect(raw).to eq("company_name - governing_law - city_for_disputes template") expect(raw).to eq("company_name - governing_law - city_for_disputes template")
expect(wizard.completed_steps?("corporate")).to eq(true) expect(wizard.completed_steps?("corporate")).to eq(true)

View File

@ -49,13 +49,13 @@ RSpec.describe PostAction do
expect(topic_user_ids).to include(codinghorror.id) expect(topic_user_ids).to include(codinghorror.id)
expect(topic_user_ids).to include(mod.id) expect(topic_user_ids).to include(mod.id)
expect(topic.topic_users.where(user_id: mod.id).pluck_first(:notification_level)).to eq( expect(topic.topic_users.where(user_id: mod.id).pick(:notification_level)).to eq(
TopicUser.notification_levels[:tracking], TopicUser.notification_levels[:tracking],
) )
expect( expect(topic.topic_users.where(user_id: codinghorror.id).pick(:notification_level)).to eq(
topic.topic_users.where(user_id: codinghorror.id).pluck_first(:notification_level), TopicUser.notification_levels[:watching],
).to eq(TopicUser.notification_levels[:watching]) )
# reply to PM should not clear flag # reply to PM should not clear flag
PostCreator.new( PostCreator.new(

View File

@ -62,11 +62,11 @@ RSpec.describe TopTopic do
TopTopic.refresh! TopTopic.refresh!
top_topics = TopTopic.all top_topics = TopTopic.all
expect(top_topics.where(topic_id: topic_1.id).pluck_first(:yearly_score)).to eq(27) expect(top_topics.where(topic_id: topic_1.id).pick(:yearly_score)).to eq(27)
expect(top_topics.where(topic_id: topic_2.id).pluck_first(:yearly_score)).to be_within( expect(top_topics.where(topic_id: topic_2.id).pick(:yearly_score)).to be_within(
0.0000000001, 0.0000000001,
).of(18.301029995664) ).of(18.301029995664)
expect(top_topics.where(topic_id: topic_3.id).pluck_first(:yearly_score)).to be_within( expect(top_topics.where(topic_id: topic_3.id).pick(:yearly_score)).to be_within(
0.0000000001, 0.0000000001,
).of(10.602059991328) ).of(10.602059991328)
@ -84,11 +84,11 @@ RSpec.describe TopTopic do
TopTopic.refresh! TopTopic.refresh!
top_topics = TopTopic.all top_topics = TopTopic.all
expect(top_topics.where(topic_id: topic_1.id).pluck_first(:yearly_score)).to eq(27) expect(top_topics.where(topic_id: topic_1.id).pick(:yearly_score)).to eq(27)
expect(top_topics.where(topic_id: topic_2.id).pluck_first(:yearly_score)).to be_within( expect(top_topics.where(topic_id: topic_2.id).pick(:yearly_score)).to be_within(
0.0000000001, 0.0000000001,
).of(18.301029995664) ).of(18.301029995664)
expect(top_topics.where(topic_id: topic_3.id).pluck_first(:yearly_score)).to be_within( expect(top_topics.where(topic_id: topic_3.id).pick(:yearly_score)).to be_within(
0.0000000001, 0.0000000001,
).of(11.2041199826559) ).of(11.2041199826559)
@ -106,11 +106,11 @@ RSpec.describe TopTopic do
TopTopic.refresh! TopTopic.refresh!
top_topics = TopTopic.all top_topics = TopTopic.all
expect(top_topics.where(topic_id: topic_1.id).pluck_first(:yearly_score)).to eq(69) expect(top_topics.where(topic_id: topic_1.id).pick(:yearly_score)).to eq(69)
expect(top_topics.where(topic_id: topic_2.id).pluck_first(:yearly_score)).to be_within( expect(top_topics.where(topic_id: topic_2.id).pick(:yearly_score)).to be_within(
0.0000000001, 0.0000000001,
).of(33.301029995664) ).of(33.301029995664)
expect(top_topics.where(topic_id: topic_3.id).pluck_first(:yearly_score)).to be_within( expect(top_topics.where(topic_id: topic_3.id).pick(:yearly_score)).to be_within(
0.0000000001, 0.0000000001,
).of(10.602059991328) ).of(10.602059991328)
@ -128,11 +128,11 @@ RSpec.describe TopTopic do
TopTopic.refresh! TopTopic.refresh!
top_topics = TopTopic.all top_topics = TopTopic.all
expect(top_topics.where(topic_id: topic_1.id).pluck_first(:yearly_score)).to eq(30) expect(top_topics.where(topic_id: topic_1.id).pick(:yearly_score)).to eq(30)
expect(top_topics.where(topic_id: topic_2.id).pluck_first(:yearly_score)).to be_within( expect(top_topics.where(topic_id: topic_2.id).pick(:yearly_score)).to be_within(
0.0000000001, 0.0000000001,
).of(21.301029995664) ).of(21.301029995664)
expect(top_topics.where(topic_id: topic_3.id).pluck_first(:yearly_score)).to be_within( expect(top_topics.where(topic_id: topic_3.id).pick(:yearly_score)).to be_within(
0.0000000001, 0.0000000001,
).of(10.602059991328) ).of(10.602059991328)
@ -144,11 +144,11 @@ RSpec.describe TopTopic do
TopTopic.refresh! TopTopic.refresh!
top_topics = TopTopic.all top_topics = TopTopic.all
expect(top_topics.where(topic_id: topic_1.id).pluck_first(:yearly_score)).to eq(27) expect(top_topics.where(topic_id: topic_1.id).pick(:yearly_score)).to eq(27)
expect(top_topics.where(topic_id: topic_2.id).pluck_first(:yearly_score)).to be_within( expect(top_topics.where(topic_id: topic_2.id).pick(:yearly_score)).to be_within(
0.0000000001, 0.0000000001,
).of(18.301029995664) ).of(18.301029995664)
expect(top_topics.where(topic_id: topic_3.id).pluck_first(:yearly_score)).to be_within( expect(top_topics.where(topic_id: topic_3.id).pick(:yearly_score)).to be_within(
0.0000000001, 0.0000000001,
).of(10.602059991328) ).of(10.602059991328)
end end

View File

@ -53,8 +53,8 @@ RSpec.describe TopicLink do
TopicLink.extract_from(post) TopicLink.extract_from(post)
# we have a special rule for images title where we pull them out of the filename # we have a special rule for images title where we pull them out of the filename
expect(topic.topic_links.where(url: png).pluck_first(:title)).to eq(png_title) expect(topic.topic_links.where(url: png).pick(:title)).to eq(png_title)
expect(topic.topic_links.where(url: non_png).pluck_first(:title)).to eq("amazing") expect(topic.topic_links.where(url: non_png).pick(:title)).to eq("amazing")
expect(topic.topic_links.pluck(:url)).to contain_exactly( expect(topic.topic_links.pluck(:url)).to contain_exactly(
png, png,

View File

@ -64,7 +64,7 @@ RSpec.describe PostAlerter do
PostAlerter.post_created(reply2) PostAlerter.post_created(reply2)
# we get a green notification for a reply # we get a green notification for a reply
expect(Notification.where(user_id: pm.user_id).pluck_first(:notification_type)).to eq( expect(Notification.where(user_id: pm.user_id).pick(:notification_type)).to eq(
Notification.types[:private_message], Notification.types[:private_message],
) )
@ -104,7 +104,7 @@ RSpec.describe PostAlerter do
) )
PostAlerter.post_created(op) PostAlerter.post_created(op)
expect(Notification.where(user_id: user.id).pluck_first(:notification_type)).to eq( expect(Notification.where(user_id: user.id).pick(:notification_type)).to eq(
Notification.types[:private_message], Notification.types[:private_message],
) )
end end

View File

@ -233,7 +233,7 @@ RSpec.describe UserMerger do
[group1, group2, group3].each do |g| [group1, group2, group3].each do |g|
owner = [group1, group3].include?(g) owner = [group1, group3].include?(g)
expect(GroupUser.where(group_id: g.id, user_id: target_user.id, owner: owner).count).to eq(1) expect(GroupUser.where(group_id: g.id, user_id: target_user.id, owner: owner).count).to eq(1)
expect(Group.where(id: g.id).pluck_first(:user_count)).to eq(2) expect(Group.where(id: g.id).pick(:user_count)).to eq(2)
end end
expect(GroupUser.where(user_id: source_user.id).count).to eq(0) expect(GroupUser.where(user_id: source_user.id).count).to eq(0)
end end