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.