ranked-model icon indicating copy to clipboard operation
ranked-model copied to clipboard

Allow for scope with argument to access model instance

Open JeanMertz opened this issue 11 years ago • 3 comments

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 avatar May 18 '13 12:05 JeanMertz

@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 avatar May 21 '13 14:05 mixonic

@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

JeanMertz avatar May 21 '13 15:05 JeanMertz

+1 on this.

osbornebrook avatar Jul 31 '13 12:07 osbornebrook

Closing due to age.

brendon avatar Jun 04 '24 04:06 brendon