With this refactor, I'm adding documentation and favoring using a common
method that wasn't available at the time of implementation.
In [this commit][1] we had logic that said "if we have a singular tag
use the cache" otherwise use the join. At that time, the implementation
of [Article.cached_tag_with][previous] was as follows, allowing only a
singular tag:
```ruby
scope :cached_tagged_with, ->(tag) { where("cached_tag_list ~* ?",
"^#{tag},| #{tag},|, #{tag}$|^#{tag}$") }
```
The [current implementation][current], as of writing this, allows for
multiple tags and is as follows:
```ruby
scope :cached_tagged_with, lambda { |tag|
case tag
when String, Symbol
# In Postgres regexes, the [[:<:]] and [[:>:]] are equivalent to "start of
# word" and "end of word", respectively. They're similar to `\b` in Perl-
# compatible regexes (PCRE), but that matches at either end of a word.
# They're more comparable to how vim's `\<` and `\>` work.
where("cached_tag_list ~ ?", "[[:<:]]#{tag}[[:>:]]")
when Array
tag.reduce(self) { |acc, elem| acc.cached_tagged_with(elem) }
when Tag
cached_tagged_with(tag.name)
else
raise TypeError, "Cannot search tags for: #{tag.inspect}"
end
}
```
Given that we are content to use the cached tag in the singular tag
case, it seems safe to say that we're comfortable using it in the
multiple tag case.
[1]:af5a391429 (diff-24503fd25ed68e6ebceee4951bc4f9b255b197278d4aa9d86ef9d5afe3f26bea)
[previous]:af5a391429/app/models/article.rb (L151)
[current]:98e97e7aa8/app/models/article.rb (L212-L227)
48 lines
2 KiB
Ruby
48 lines
2 KiB
Ruby
module Articles
|
|
class ActiveThreadsQuery
|
|
DEFAULT_OPTIONS = {
|
|
tags: ["discuss"],
|
|
time_ago: nil,
|
|
count: 10
|
|
}.with_indifferent_access.freeze
|
|
|
|
MINIMUM_SCORE = -4
|
|
|
|
# Get the "plucked" attribute information for the article thread.
|
|
#
|
|
# @param relation [ActiveRecord::Relation] the original Article scope
|
|
# @param options [Hash]
|
|
# @option options [Array<String>] :tags which tags to select
|
|
# @option options [NilClass, String, ActiveSupport::TimeWithZone] :time_ago
|
|
# @option options [Integer] :count the number of records to pluck
|
|
#
|
|
# @return [Array<Array>] The inner array is the plucked attribute
|
|
# values for the selected articles. Which means be mindful
|
|
# of the order you pass for attributes.
|
|
#
|
|
# @note The order of attributes and behavior of this method is from
|
|
# past implementations. A refactor to consider would be to
|
|
# create a data structure.
|
|
#
|
|
# @see `./app/views/articles/_widget_list_item.html.erb` for the
|
|
# importance of maintaining position of these parameters.
|
|
def self.call(relation: Article.published, **options)
|
|
options = DEFAULT_OPTIONS.merge(options)
|
|
tags, time_ago, count = options.values_at(:tags, :time_ago, :count)
|
|
|
|
relation = relation.limit(count)
|
|
relation = relation.cached_tagged_with(tags)
|
|
relation = if time_ago == "latest"
|
|
relation = relation.where(score: MINIMUM_SCORE..).presence || relation
|
|
relation.order(published_at: :desc)
|
|
elsif time_ago
|
|
relation = relation.where(published_at: time_ago.., score: MINIMUM_SCORE..).presence || relation
|
|
relation.order(comments_count: :desc)
|
|
else
|
|
relation = relation.where(published_at: 3.days.ago.., score: MINIMUM_SCORE..).presence || relation
|
|
relation.order("last_comment_at DESC NULLS LAST")
|
|
end
|
|
relation.pluck(:path, :title, :comments_count, :created_at)
|
|
end
|
|
end
|
|
end
|