A magical feature of database-backed models is the way objects are linked to one another through associations, setting bidirectional assignments between them.
Let's have a quick recap on how ActiveRecord associations work. These associations let you specify something like.
class Driver < ActiveRecord::Base has_one :car end class Car < ActiveRecord::Base belongs_to :driver end
If you assign the driver to the car and save, you will magically have the objects linked to one another, like this:
driver = Driver.first car = Car.first # Assign the driver to the car and save car.driver = driver car.save # Check the bidirectional assignment car.driver #=> the driver driver.car #=> the car
This is pretty much taken for granted when dealing with models in Rails for example, but let's look at what we have to do when working with simple ruby classes and objects - POROs (Plain Old Ruby Objects).
# Create some simple classes with accessors for each other class Driver attr_accessor :car end class Car attr_accessor :driver end # Create two simple objects driver = Driver.new car = Car.new # Assign a driver to the car car.driver = driver # Check the accessors car.driver #=> the driver driver.car #=> nil ... nope, not magically set
If we want for both objects to be 'associated', we must set each to one another explicitly, like this:
# Assign a driver to the car AND viceversa car.driver = driver driver.car = car
Typing that extra line of code seems somewhat annoying when we are actually trying to associate the two objects, and it would be even worse if we were talking about collections of objects.
How about writing the following piece of ruby code?
class Driver include SomeMagicalModule has_one :car end class Car include SomeMagicalModule belongs_to :driver end
With the active_poro gem, you can have exactly this. There are already many gems that do a great job helping with model-like functionality, however, it wasn't clear how would associations could work for POROs.
If you want to quickly try this out, check out the following:
Install the gem
gem install active_poro
and now you can do something like
require 'active_poro' class Driver include ActivePoro::Model has_one :car end class Car include ActivePoro::Model belongs_to :driver end
Not only that, but you can also set a has_many association too!
require 'active_poro' class Driver include ActivePoro::Model has_many :cars end class Car include ActivePoro::Model belongs_to :driver end
When this is set, any objects that you include ActivePoro::Model on will find the reflected association and complete the bidirectional assignment for you. In fact, you only need to include the ActivePoro::Associations module, since its the only thing active_poro provides by now.
From the example above, active_poro creates for you some convenience methods for adding and removing associated objects when using a has_many association.
driver = Driver.new car_A = Car.new car_B = Car.new driver.cars = [car_A] driver.add_car car_B driver.remove_car car_A
At the time of this writing, we are not proxying the collection so the following would not update the association on both sides
driver.cars << car_C
However we could implement an invisible proxy to allow these kind of behaviour.
If you find this useful, be sure to check out the README and source on github. We are more than happy to receive your comments below!