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!
Cheers!