Intro to OOP: Ruby 'One to Many' vs 'Many to Many'
Tags: oop, ruby, class, instance
Published: 2019-10-22 17:19:48

As a Ruby beginner, I started off learning the basics - hashes, arrays and methods. These building blocks will continue to show up again and again. But how do these 'building blocks' come together? 

When faced with the challenge of modeling real-world paradigms, Classes opened new doors to the possibilities of code. In Ruby, the use of Classes to create relationships is known as Object Oriented Programming (OOP). An individual instance of a Class is known as an Object or Instance.

When designing the relationships among Classes I find it helpful to first distinguish: am I dealing with a 'one to many' relationship or a 'many to many' relationship? 

An example of a 'one to many' relationship is: a sports team and its players. A player only has one team (let's disregard outliers like Bo Jackson) and a team has many players. To connect classes that fall into this category, I would add an attribute, intended to store an entire object of the 'one' class, to the 'many' class. For our team example this means adding a 'team' attribute, intended to store a team object, to the Player class.

```ruby
class Player
	attr_accessor :team
	attr_reader :name, :position
	@@all = []

	def initialize(name, position)
		@name = name 
		@position = position
		@@all << self
	end
end

class Team
	attr_reader :name, :city
	@@all = []

	def initialize(name, city)
		@name = name
		@city = city
		@@all << self
	end

	def players
        Player.all.select { |player| player.team == self }
	end
end
```
With the above 'players' method inside the Team class I am able to find a team's roster (all of its players), and this is possible because of the 'team' attribute inside the Player class.


An example of a 'many to many' relationship is: a social media app and its users. A person uses many social media apps and a social media app has many users. This scenario calls for a Join class. Let's run through how it looks...

```ruby
class User
	attr_reader :name, :email
	@@all = []

	def initialize(name, email)
		@name = name
		@email = email
		@@all << self
	end
end

class App
	attr_reader :name, :url
	@@all = []

	def initialize(name, url)
		@name = name
		@url = url
		@@all << self
	end
end

class Account
	attr_reader :user, :app
	attr_accessor :username, :password
	@@all = []

	def initialize(user, app, username, password)
		@user = user
		@app = app
		@username = username
		@password = password
		@@all << self
	end
end
```

In the above example, I created three classes: a User class, an App class and an Account class. The User class only captures information about a person: her/his name & email. The App class only captures information about a social media app: its name and url. And the Account class captures all User and App information (by initializing an Account object with a User object and an App object), as well as information that is specific to an account: its username and password. A user does not inherently know about the app(s) she/he uses and an app does not inherently know about its users, but they are connected through accounts. 

With an 'Accounts' join class in place, I am now able to keep track of all user/app relationships, and call on methods belonging to either class. For example, if I wanted to see all of the apps for a specific user I could write...

```ruby
class User	
	...
	def apps
		my_accts = Account.all.select {|acct| acct.user == self}
		my_accts.map {|acct| acct.app}
	end
end
```

Designing your Class relationships from the start will allow you to hit the ground running when you're ready to write your code. And realizing when you're dealing with a 'One to Many' vs a 'Many to Many' relationship should simplify your design process.