ranked-model
ranked-model copied to clipboard
Allow for scope with argument to access model instance
I was wondering if you see a legitimate use case to allow for scopes that accept an argument, which represent the current instance RankedModel
is working with.
For example, I made this modification:
@@ -198,7 +199,11 @@ module RankedModel
@finder ||= begin
_finder = instance_class
if ranker.scope
- _finder = _finder.send ranker.scope
+ if _finder.method(ranker.scope).arity == 0
+ _finder = _finder.send ranker.scope
+ else
+ _finder = _finder.send ranker.scope, instance
+ end
end
case ranker.with_same
when Symbol
This allows me to do something like this:
class Story < ActiveRecord::Base
include RankedModel
ranks :priority, scope: :same_swimlane_as
scope :same_swimlane_as, ->(story) do
stories = self.where(column_id: story.column_id)
# find all stories that have swimlane labels
labeled_stories = stories.joins("INNER JOIN swimlanes ON
((string_to_array(swimlanes.data -> 'labels', ',')) &&
(string_to_array(tracker -> 'labels', ',')))")
# get stories with same swimlane labels as current story
if sllabels = Label.new(story).swimlaneizers.join(',') and sllabels.present?
labeled_stories.where("string_to_array(swimlanes.data -> 'labels', ',') @> '{#{sllabels}}'")
# get only stories without swimlane labels for 'default' swimlane...
elsif labeled_stories.any?
stories.where("stories.id NOT IN (?)", labeled_stories)
# ...or just all labels if no non-default swimlane stories exist in column
else
stories
end
end
end
This is just a small (rough) example of how this could be used. At first I checked for arity == 1
, but I found out that the scope actually returned an arity of -1
, meaning it accepts a variable number of arguments. Perhaps this is because it's a lambda, which works differently?
Anyway, I found it useful, and use it in my fork, if this is something you want to implement (and possibly know of a cleaner way to do so), that would be great :+1:
@JeanMertz Crazy! I'm concerned about changing the way scopes normally work- but I suppose this would be limited to only scopes being used by RankedModel. I'd like to see a version that allows definition of a scope that accepts multiple arguments, but expects instance to be the first. Just so you could write a scope of:
scope :same_swimlane_as, ->(story, sometimes_an_argument) do
# test for sometimes_an_argument and set a default if it isn't there
That means the if is more like:
if _finder.method(ranker.scope).arity != 0
_finder = _finder.send ranker.scope, instance
else
_finder.send ranker.scope
end
Possibly? I'd merge that if we had a PR and test for it.
@mixonic thank you for your feedback. I will pick this up somewhere this (or next) week and come up with a PR + tests.
Regarding your proposed changes, I fail to see how that would work.
First of all, how would you send it an optional argument if the scope is only used by RankedModel
itself? You never get the chance to set this optional argument unless you monkey patch the gem? Or is your intent for the scope to be usable outside of the RankedModel
use-case?
I guess that would work, but I think scopes like these would be very specific to RankedModel and I would prefer to refactor this into smaller parts if I where to reuse it. For example, something like I did here: app/models/story.rb#L12-L17
Second, I don't see a difference in your if/ese compared to mine, except that you flipped the conditions:
if != 0 x; else y
vs if == 0 y; else x
+1 on this.
Closing due to age.