-
-
Save kaspth/54c2541309704e054bca2be5ace3ebee to your computer and use it in GitHub Desktop.
class Developer | |
has_object :search_scorer, after_save_commit: :rebuild_later # Forward callback onto context object. | |
end | |
# From my https://github.com/kaspth/active_record-associated_object gem. | |
# app/models/developer/search_scorer.rb | |
class Developer::SearchScorer < ActiveRecord::AssociatedObject | |
performs :rebuild # Adds `rebuild_later` to automatically generate a job to run the scoring calculation in. | |
def rebuild | |
record.update! search_score: new_score | |
end | |
private | |
def new_score | |
small, medium, large, x_large = 5, 10, 20, 30 | |
score = [] | |
score << medium if record.scheduling_link? | |
score << large if record.source_contributor? | |
score << x_large if record.recently_added? | |
score << medium if record.bio_length > 500 | |
score << -large if record.bio_length < 50 | |
score << large if record.profile_updated_at&.before? 1.months.ago | |
score << medium if record.profile_updated_at&.before? 3.months.ago | |
score << -medium if record.profile_updated_at&.after? 6.months.ago | |
score << large if record.conversations_count.positive? && record.response_rate >= Developer::HasBadges::HIGH_RESPONSE_RATE_CUTOFF | |
score << -large if record.conversations_count.positive? && record.response_rate <= Developer::HasBadges::LOW_RESPONSE_RATE_CUTOFF | |
score.sum | |
end | |
end |
# app/models/developers/search_score.rb | |
module Developers::SearchScore | |
extend ActiveSupport::Concern | |
included do | |
small, medium, large, x_large = 5, 10, 20, 30 | |
scores :scheduling_link?, by: medium, if: :present? | |
scores :source_contributor?, by: large, if: :present? | |
scores :recently_added?, by: x_large, if: :present? | |
scores :bio_length, by: medium, if: -> { _1 > 500 } | |
scores :bio_length, by: -large, if: -> { _1 < 50 } | |
scores :profile_updated_at, by: large, if: -> { _1&.before? 1.months.ago } | |
scores :profile_updated_at, by: medium, if: -> { _1&.before? 3.months.ago } | |
scores :profile_updated_at, by: -medium, if: -> { _1&.after? 6.months.ago } | |
scores :response_rate, by: large, if: -> { conversations_count.positive? && _1 >= HasBadges::HIGH_RESPONSE_RATE_CUTOFF } | |
scores :response_rate, by: -large, if: -> { conversations_count.positive? && _1 <= HasBadges::LOW_RESPONSE_RATE_CUTOFF } | |
before_save :recalculate_search_score | |
end | |
class_methods do | |
attr_reader :scorings | |
def scores(attribute, by:, **options) | |
(@scorings ||= []) << -> { by if instance_exec(public_send(attribute), &options.fetch(:if)) } } | |
end | |
end | |
private | |
def recalculate_search_score | |
self.search_score = self.class.scorings.filter_map(&:call).sum | |
end | |
end |
@swanson ah yep, that's true
Deleted the linking tweet, since this approach ended up being flawed 👍
Ah, man! I really like this approach though.
I wonder if the same scores
could be a declarative approach to how each score contributes, then it is calculated fresh every time.
I wonder if the same
scores
could be a declarative approach to how each score contributes, then it is calculated fresh every time.
yeah, good point. I updated it to do that. Also added an alternate just because I was curious to see what it would look like modeled as a domain context object using a gem I've got for that.
Oh, nice! I might be missing something, but how does this line work?
self.search_score = self.class.scorings.filter_map(&:call).sum
It keeps trying to call, for example, scheduling_link
on the class, not the instance.
If I can get this to work with the existing tests then I'd love to merge it in!
Yeah, it's a little finicky unfortunately. I fixed it up here joemasilotti/railsdevs.com#858
Amazing, thanks @kaspth!
Careful, if I change my bio it's going to keep adding
10
to my score every time I save the changes :)