-
-
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
scorescould 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).sumIt 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
10to my score every time I save the changes :)