Tag: ActiveRecord::Relation

[ActiveRecord] Get a polymorphic owner class out of the owned object

The question is: how can we retrieve a owners class of a polymorphic association without loading the owner object into memory?

class Source < ApplicationRecord
  self.has_one :state,
    dependent: :delete,
    as: :stateable
end

class State < ApplicationRecord
  belongs_to :stateable,
    polymorphic: true
end

Solution

There are (at least) two ways to do this. First one is not recommended (as it might fail with multiple namespaces with same class names, etc:

State.last.stateable_type.constantize #=> Source

or a better one that will work out of any scope:

State.last.association(:stateable).klass #=> Source

Extending ActiveRecord association to cleanup your associations code

For some reason not many RoR developers know that they can extend ActiveRecord associations. This feature can be a great way to cleanup any relational code that is dependent on a parent resource (or to just add a simple functionalities to scopes). Here's a really simple example how you can use it:

class User < ActiveRecord::Base
  module GroupActions
    # Here you work on a scope
    def reactivate!
      # I know we could use update_all - this is not the case ;)
      map(&:reactivate!)
    end
  end

  belongs_to :group

  def reactivate!
    update!(active: true)
  end
end

class Group < ActiveRecord::Base
  has_many :users, extend: User::GroupActions
end

That way you can perform actions like this one:

# Looks much better than any method like current_group.reactivate_users!
current_group.users.reactivate!

Dynamic pagination (changeable per page on a parent resource)

You can also use a small trick to access parent model and it's data. It can be useful for example when implementing a dynamic pagination model (based on a parent key value) or any other functionality that somehow depends on a relation owning model. Instead of doing something like this in your controllers:

def index
  @pictures = current_gallery.pictures.per(current_gallery.per_page).page(current_page)
end

you can leave the implementation details out of it (which in general is a good idea):

def index
  @pictures = current_gallery.pictures.paginated(current_page)
end

And this is how you can achieve such a behavior:

class Gallery < ActiveRecord::Base
  has_many :pictures,
    extend: Picture::RelationExtensions

  validates :per_page,
    numericality: { only_integer: true },
    inclusion: { in: (1..100).to_a }
end

class Picture < ActiveRecord::Base
  module RelationExtensions
    def paginated(current_page)
      # We create a picture instance that has a gallery reference already
      # because in the controller - gallery is a current scope starting point
      page(current_page).per(self.new.gallery.per_page)
    end
  end

  belongs_to :gallery, inverse_of:  :pictures
end

It's a really great idea to use approach like this also because you can use it with any standard scope:

current_gallery.active.paginated(current_page)
# or if you return an active record association you can chain methods
current_gallery.active.paginated(current_page).reactivate!

Conclusion

If you want to hide implementation details of any association related action (especially when it uses owning model fields data), using ActiveRecord association can be a really good idea.

Copyright © 2024 Closer to Code

Theme by Anders NorenUp ↑