brendon / acts_as_list

An ActiveRecord plugin for managing lists.

Home Page:http://brendon.github.io/acts_as_list/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Scoping by attribute and specific created_at date

programthis opened this issue · comments

I am trying to scope my model with an array like so (I want to scope it by a parent object and the current day):

acts_as_list scope: [:user_category, created_at: Time.zone.now.beginning_of_day..Time.zone.now.end_of_day]

The issue is that the position is always being set as 1. I've noticed that this also seems to happen even when I use one attribute within the array (when I don't use the array, it works fine though...):

acts_as_list scope: [:user_category]

Hi @programthis, before I get too far, I was wondering if you clarify your intent? If it worked, your first example would maintain a list of items each day (within a category). If you then went an edited an item on a subsequent day the scope for that item would have changed (since the day has changed) and so it would be removed from the previous list and added to the new list. Is that what you were wanting? In reality, the scope change logic wouldn't work as it relies on a solid value to query (i.e. it asks the model what the previous scope value was before it changed, then closes the gap with other items in that scope, then proceeds to open a gap in the new scope and put the item there).

You could achieve this by adding a :created_on field to your table also. Rails will auto populate this with the created date (without time). You could then just have the scope be [:user_category, :created_on] and all should work well.

If you stray from the beaten path, it is possible to have dynamic scopes like that but you have to implement your own scope change logic. I had to do this once:

acts_as_list :add_new_at => :top

def scope_condition
  ['notice_area_id = ? AND ? >= CURDATE()', notice_area_id, end_date]
end

# A custom acts_as_list scope requires a custom scoped_changed? method
def scope_changed?
  changed.include?('notice_area_id') ||
  changed.include?('end_date') && (
    changes['end_date'][0] >= Time.current.beginning_of_day &&
    changes['end_date'][1] < Time.current.beginning_of_day ||
    changes['end_date'][1] >= Time.current.beginning_of_day &&
    changes['end_date'][0] < Time.current.beginning_of_day
  )
end

def destroyed_via_scope?
  :notice_area_id == (destroyed_by_association && destroyed_by_association.foreign_key.to_sym)
end

Now that I look at code from my younger self I can see some room for improvement lol! But in this scenario, I only care to order 'current' and 'future' notices. Once they fall out of scope the position doesn't matter (as long as you can tolerate a sparse set of position values).

Hope that helps! :D

@brendon Thanks for the reply! That is exactly the functionality I'm looking for. Essentially, I have a calendar of daily "goals" so they need to be grouped by category and that specific day.

So I did initially try that approach you suggested with creating an extra date column, without time, (and just tried it again now) but I'm still facing the issue where the position is being set to one for every new goal I add.

So my code is currently:
acts_as_list scope: [:user_category, :created_on]

Again, I have noticed that even when I use the array with just user_category, I get the same result so
acts_as_list scope: [:user_category] results in position 1 for all wins but
acts_as_list scope: :user_category gives the correct position result.

Could there be an issue with the array brackets?

Oh, you know what it was. There seems to be an issue where user_category works without the brackets but user_category_id works within the brackets. Once I changed it to that, it worked correctly!

Oh yes! Sorry there is a weird legacy reason for that syntax difference unfortunately. Symbols within an array scope all need to be concrete column names. The shorthand single symbol syntax will add an _id to the end of the column. These days that's a bit old-hat as we can reflect on the association and get the foreign key more reliably that way.

Gotcha! Well, thanks for your help. That's good to know for the future!