'Rails - Returning all records for who a method returns true

I have a method:

class Role
  def currently_active
    klass = roleable_type.constantize
    actor = Person.find(role_actor_id)
    parent = klass.find(roleable_id)
    return true if parent.current_membership?
    actor.current_membership?
  end
end

I would like to return all instances of Role for who this method is true, however can't iterate through them with all.each as this takes around 20 seconds. I'm trying to use where statements, however they rely on an attribute of the model rather than a method:

Role.where(currently_active: true)

This obviously throws an error as there is no attribute called currently_active. How can I perform this query the most efficient way possible, and if possible using Active Records rather than arrays?

Thanks in advance



Solution 1:[1]

It seems impossible, in your case you have to do iterations. I think the best solution is to add a Boolean column in your table, so you can filter by query and this will be much faster.

After seeing your method after edit, it seems that it's not slow because of the loop, it is slow because Person.find and klass.find , you are doing alot of queries and database read here. (You better use associations and do some kind of eager loading, it will be much faster)

Another work-around is you can use ActiveModelSerializers , in the serializer you can get the attributes on the object based on condition. and after that you can work your logic to neglect the objects that have some kind of flag or attribute.

See here the documentation of active model serializer

Conditional attributes in Active Model Serializers

Solution 2:[2]

Wherever possible you better delegate your methods to SQL through activerecord when you're seeking better efficiency and speed and avoid iterating through objects in ruby to apply the method. I understand this is an old question but still many might get the wrong idea.

There is not enough information on current_membership? methods on associations but here's an example based on some guess-work from me:

roleables = roleable_type.pluralize
roleable_type_sym = roleable_type.to_sym

Role.joins(roleables_sym).where(" ? BETWEEN #{roleables}.membership_start_date AND #{roleables}.membership_end_date", DateTime.current).or(Role.joins(:person).where(" ? BETWEEN persons.membership_start_date AND persons.membership_end_date", DateTime.current))

so you might have to re-implement the method you have written in the model in SQL to improve efficiency and speed.

Solution 3:[3]

Try the select method: https://www.rubyguides.com/2019/04/ruby-select-method/

Role.all.select { |r| r.currently_active? }

Solution 4:[4]

The above can be shortened to Role.select(&:currently_active?)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Community
Solution 2 AlphaCarinae
Solution 3 Etan Mizrahi-Shalom
Solution 4 Hannah__