A simple guide to understanding object-oriented ruby relationships (finally!)
As a beginner, I have had the hardest time wrapping my head around the concept of object-oriented relationships in code. After weeks (or try months) of reading explainers and watching tutorial videos, I’m beginning to see the light.
Here, I’ll distill examples of belongs to, has many, and has many through relationships down to bare bones, which has helped me build comprehension. And hopefully, it’s useful to you too. Let me know?
Belongs to relationship
The belongs to relationship means that ONE object (a song, a book, a dog, etc.) has an owner (an artist, an author, a shelter, etc).
We’re able to show that in code by creating an attr_accessor for the owner.
class Dog
attr_accessor :shelter
end
class Shelter
end
The above code allows us to create a new dog and assign ownership of the dog to a shelter.
Step 1: Create new dog and assign it equal to Bandit variable.
Step 2: Create new shelter
Step 3: Assign bandit’s shelter attribute to your instance of Shelter that you just created
Voila, Bandit is now a instance of the Dog class with a shelter attribute.
bandit = Dog.new
nycshelter = Shelter.new
bandit.shelter = nycshelter #assigning the dogbandit.shelter
bandit
=> #<Dog:0x000055b18e671ca0 @shelter=#<Shelter:0x000055b18e671c78>>
But there’s trouble in paradise. At this point, the instance of Shelter does not recognize or see the dog it owns.
Here’s how we know that:
nycshelter.dogs
=> undefined method `dogs’ for #<Shelter:0x00005557b6fca1f8>
(repl):15:in `<main>’
When we ask nycshelter, an instance of Shelter, what dogs it has many of, it has no clue. Looking at the error, we have a clue, which points to what we need to do next. We need to define a method for dogs for the shelter. But every instance of shelter in general should probably know what dogs it has many of.
Has Many Relationships
Here’s the flip side of the belongs to relationship. A shelter has many dogs or you could say the shelter has a collection of dogs.
To establish a has many relationship, our owner instance needs an empty array to store its collection of items (in this case, dogs), where every instance of the dog that’s instantiated can be added.
class Shelter
attr_accessor :dogs, :name def initialize(name)
@name = name
@dogs = []
end
end
Here, we’re just setting up a new shelter named brooklynshelter:
brooklynshelter = Shelter.new(brooklynshelter)
=> #<Shelter:0x000055a164d8b1d8 @name=nil, @dogs=[]>
We’re going to reset our dog (belongs to) and shelter (has many) relationship.
brooklynshelter = Shelter.new(brooklynshelter)
snacks = Dog.new
snacks.shelter = brooklynshelter
snacks
=> #<Dog:0x00005614828bf708 @shelter=#<Shelter:0x00005614828bf758 @name=nil, @dogs=[]>>
So, we can that snacks now belongs to brooklynshelter, an instance of Shelter, which has a collection (empty array) of dogs.
brooklynshelter.dogs
=> []
When we ask brooklynshelters for its dogs attribute, it’s still showing up as being empty. Because there’s another essential step we’re forgetting: ADDING THE INSTANTIATED DOG INTO THE EMPTY ARRAY. We’re going to do that with an #add_dog method
brooklynshelter = Shelter.new(brooklynshelter)
snacks = Dog.new
snacks.shelter = brooklynshelterbrooklynshelter.add_to_shelter(snacks)
brooklynshelter
=> #<Shelter:0x0000563fc0797240 @name=nil, @dogs=[#<Dog:0x0000563fc07971f0 @shelter=#<Shelter:0x0000563fc0797240 …>>]>
Now, the flip side of the relationship — the has many relationship — is complete. The instance of Shelter — brooklynshelter — knows it has a dog instance whose shelter attribute is equal to the shelter.
Has Many Through Relationship
One way I get my brain to comprehend complicated Ruby concepts is by breaking them down into simple rules or its smallest components. I’m going to try that, here, with the has many relationship.
The has many through relationship describes the connection between 3 classes, wherein 1 of 3 classes provides an indirect link (or relationship) between the other 2.
- One item will be the middle man and provides an indirect relationship between or through two objects
- The shared item will belong to the other two items
- The other two items will have many of the shared item
For example:
→shelter → dogs→ owner
In this relationship:
* shelter has many dogs
* dog belongs to shelter and owner
* owners has many dogs
* through dogs, shelter has many owners and owners has many shelters
class Shelter #has many dogs
attr_accessor :name, :city
@@all = []def initialize (name, city)
@name = name
@city = city
@dogs = []
@@all << self
enddef add_dog(dog) #always should have an argument so we can pass in an instance of dog
dog.shelter = self
@dogs << dog
enddef dogs
@dogs
enddef self.all
@all
end
end
class Dog #belong to a shelter & an owner
attr_accessor :name, :age, :breed, :shelter, :owner
@@all = []def initialize (name, age, breed)
@name = name
@age = age
@breed = breed
@@all << self
enddef self.all
@all
end
end
class Owner #has many dogs
attr_accessor :name, :age, :dogs
@@all = []def initialize (name, age)
@name = name
@age = age
@dogs = []
@shelters = []
@@all << self
enddef add_dog(dog)
@dogs << dog
dog.owner = self
enddef shelters
self.dogs.each do |dog|
@shelters << dog.shelter if dog.shelter
end
@shelters
end
def self.all
@@all
end
end
With all this, we can invoke:
ruffles = Dog.new(“ruffles”, 1, “corgi”)
queensshelter = Shelter.new(“Queens Humane Society”, “NYC”)
ruffles.shelter = queensshelter
ruffles.shelter
queensshelter.add_dog(ruffles)joe = Owner.new(“joe”, 30)
joe.add_dog(ruffles)joe.shelters
joe.shelters.each {|shelter| puts shelter.name}
=> Queens Humane Society
=> [#<Shelter:0x00005612a3437d38 @name=”Queens Humane Society”, @city=”NYC”, @dogs=[#<Dog:0x00005612a3437db0 @name=”ruffles”, @age=1, @breed=”corgi”, @shelter=#<Shelter:0x00005612a3437d38 …>, @owner=#<Owner:0x00005612a3437cc0 @name=”joe”, @age=30, @dogs=[#<Dog:0x00005612a3437db0 …>], @shelters=[…]>>]>, #<Shelter:0x00005612a3437d38 @name=”Queens Humane Society”, @city=”NYC”, @dogs=[#<Dog:0x00005612a3437db0 @name=”ruffles”, @age=1, @breed=”corgi”, @shelter=#<Shelter:0x00005612a3437d38 …>, @owner=#<Owner:0x00005612a3437cc0 @name=”joe”, @age=30, @dogs=[#<Dog:0x00005612a3437db0 …>], @shelters=[…]>>]>]