Bits for hackers.

Step by Step Guide to Polymorphic Associations in Rails

What are polymorphic associations

Typically, in Rails, when you need to make one class belong to multiple classes, you have to add a separate belongs_to method in the class each time. For example, if we think about the domain model of a generic social media web application, one can imagine that a comment can be made on a photo, a wall post, an event, an article, etc.

If we were to model this without polymorphic associations, we could do one of two things:

  1. Create a single comments model with multiple belongs_to for each association:
    1
    2
    3
    4
    5
    6
    
    class Comment < ActiveRecord::Base
      belongs_to :picture
      belongs_to :post
      belongs_to :event
      belongs_to :article
    end
    
    The problem with this method is that your table will have a number of foreign keys for each of the `belongs_to`; however, only one of them will actually have a value at one time - a comment can only belong to one of these other models at one time.
  2. Create multiple different comments models:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    class PictureComment < ActiveRecord::Base
      belongs_to :picture
    end
    
    class PostComment < ActiveRecord::Base
      belongs_to :post
    end
    
    class EventComment < ActiveRecord::Base
      belongs_to :event
    end
    
    class ArticleComment < ActiveRecord::Base
      belongs_to :article
    end
    
    Going this route, we no longer have the issue where we have a number of unused foreign keys; however, the issue is that there are now a lot of models that all more or less do the same thing and act the same way.

The solution to this is to use polymorphic associations with one model - this is a model can belong to more than one other model, on a single association.

How to create a polymorphic association

In order to create a polymorphic association, we create a comments model; however, rather than making it belong to each of the other classes, we make it belong_to a generic commentable object.

1
2
3
class Comment < ActiveRecord::Base
  belong_to :commentable, :polymorphic => true
end

Now, using this method, we can make any number of models “commentable” - meaning that they can be commented on. All we have to do is make each class have many comments:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Picture < ActiveRecord::Base
  has_many :comments, as: commentable
end

class Post < ActiveRecord::Base
  has_many :comments, as: commentable
end

class Event < ActiveRecord::Base
  has_many :comments, as: commentable
end

class Article < ActiveRecord::Base
  has_many :comments, as: commentable
end

A helpful tip

Using the polymorphic association method above, we now have only one comments model and our code is pretty DRY. However, the controller could still use some DRYing up. For example, when we’re looking to create a particular comment, we have to find the appropriate commentable to build the new comment with:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class CommentsController < ApplicationController
  def create
      if params[:picture_id]
          @commentable = Picture.find(params[:picture_id])
      elsif params[:post_id]
          @commentable = Post.find(params[:post_id])
      elsif params[:event_id]
          @commentable = Event.find(params[:event_id])
      elsif params[:article_id]
          @commentable = Article.find(params[:article_id])
      end

      @comment = @commentable.comments.build(comment_params)
  end
end

You can imagine that every time you need to find the commentable that the comment is associated with, you would have to run all of that conditional logic. However, Ryan Bates’ Railcast on polymorphic associations has a really nice method that can be added to DRY up your code in the following manner:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class CommentsController < ApplicationController    
  def create
      @commentable = find_commentable
      @comment = @commentable.comments.build(comment_params)
  end

  private

  def find_commentable
    params.each do |name, value|
      if name =~ /(.+)_id$/
        return $1.classify.constantize.find(value)
      end
    end
    nil
  end
end

This is a really nice piece of Ruby programming that uses regex to look through the params and find any params that end in “_id”. It then takes those params, uses the classify method to format the string to look like a class. It then uses the constantize method to find a declared constant with the name specified in the string. Finally, it looks through the class for the ID value in the key/value params hash.

Making Turbolinks and Jquery Co-exist in a Rails App

What are turbolinks

Turbolinks is a feature that was introduced with Rails 4 (but work with Rails 3 as well) , which greatly increases the speed and performance of a Rails App. Turbolinks allows an application to load new pages at high speeds by using Javascript to call the title (in the head of your html) and body of your app every time you click on a link - it doesn’t actually query for the entire html document over and over again every time you click a link. If you’ve ever worked with AJAX before, Turbolinks works in a very similar way where it makes a call to your server to load up the content that you requested. Therefore, you don’t technically get a full page refresh when you work with turbolinks even though it looks like you do.

Issues can arise when using JQuery and turbolinks…

If you have a Rails app that utilizes a lot of JQuery, you may start to notice that some that you’re using JQuery with don’t work as they should when you click between links of pages. This is because traditionally, a lot of JQuery requires some function to fire off after receiving the document ready event:

1
2
3
$(document).ready(function(){
  // all your jquery goes here after the document ready event is fired off
})

Because turbolinks don’t technically refresh the page every time you click on a link, the document ready event will never fire off and therefore never run any of the code within you document ready. If you’re not sure whether your app has these symptoms or not, try refreshing the page yourself and seeing if the page now works as it should - if it does, you can be sure that it is a turbolinks issue.

So how can you fix this?

There are a number of ways to do this. My favorite method is to use the JQuery Turbolinks gem. With this gem, all you have to do is:

  1. Add it to your gemfile with `gem ‘jquery-turbolinks’`
  2. Add it to your javascript manifest file in the specified order below:
    1
    2
    3
    4
    5
    6
    7
    
    //= require jquery
    //= require jquery.turbolinks
    //= require jquery_ujs
    //
    // ... your other scripts here ...
    //
    //= require turbolinks
    

Another method to fix this issue is to not bind to the page load event but rather to bind to other events on the document such as page:load. Other events that you can bind to as well (depending on what you are trying to do) include:

  • page:before-change
  • page:fetch
  • page:receive
  • page:before-unload
  • page:change
  • page:update

Using either of these methods should fix all of your turbolinks and JQuery issues while still allowing you to take advantage of the speed that turbolinks provides.

Gitting Your App Ready With .gitignore

Though Git is a revolutionary tool to enable version control and collaboration between developers, there are a lot of intricacies related to Git that go well beyond just adding, merging, and commiting. Getting a strong handle on Github allows you to more seamlessly build an application by yourself or with other collaborators.

One VERY important aspect to get a handle on very early in one’s career as a programmer is the .gitignore file that is created once you initialize your application (using the command git init). Essentially, the gitignore file is a document that keeps track of all of the files that you DON’T want to track with Git. For example, when you type the command git add . into your terminal, you tell git to start tracking all of your files. Then when you commit these changes and push these changes up to a repository (e.g., Github), all of the files in your application get pushed to the repository.

However, there are some files that you do not want to push up to any sort of public repository. These are files that:

  1. Contain sensitive information about your application
  2. Contain temporary data or data that is only specific to your local environment (these tend to create a lot annoying of merge conflicts when you are collaborating with others)

So what are these files specifically? As the majority of my web development has been focused on Rails apps, here is a good model for a simple .gitignore file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# Your log files are not necessary to push up to a respository. Additionally, it can be 
# a privacy risk to provide others with an inside look at how your app functions through
# your log. 

/log/*

#These are any temporary files generated by your app when you run it locally. These can
#include cached sprockets files. Pushing this up while collaborating with others will
#lead to a number of annoying merge conflicts.
/tmp/*

# It can be very annoying when collaborating with others in development to not add 
# this to the gitignore. It leads to a number of merge conflicts since your databases 
# will be different when in development. Additionally, it is a privacy risk to give 
# up your database structure and data.
/db/*.sqlite3

# Add these to make sure that any app tokens and that your Rails app secret are not revealed and pushed to a repository
config/initializers/secret_token.rb
config/secrets.yml

# This file shouldn't be pushed up. It is regenerated when you bundle install an application.
# Including this often leads to merge conflicts which you have to fix; however, you should
# never have to do any work within the Gemfile.lock
Gemfile.lock

#Add these files if you are running any rspec or capybara tests on your application.
capybara-*.html
.rspec
/spec/tmp

If you’re like me and you like sticking to your terminal when interacting with git, you can add files to your gitignore using the following method: echo file_or_folder_name >> .gitignore

It is good practice to add all your gitignore files at the beginning of every Rails app so get in the habit of setting this up early. The template provided above is a very barebones gitignore file. As you develop your application, always try to think whether a file really needs to be tracked by git or whether it is better left untracked.

Transitioning to JavaScript From Ruby: Prototypes

Having gotten very comfortable with the beauty and elegance of Ruby over the past few months, making the transition to JavaScript was anything but painless. All of the small things such as having declare variables, remembering to insert semi-colons at the end of the line, and having to build out methods that felt like they should be part of the standard library (luckily underscore.js eased some of the pain here) made my life quite difficult. However, the hardest thing, I found, was getting into the JavaScript mindset of prototypes rather than classes and instances like we use in Ruby.

The main differences between these two approaches to object orientation are around distinctions between class objects and instance objects. In Ruby (and other similar, class-based languages), there is a clear distinction between instances and classes. However, in JavaScript, there is no such distinction - all objects are instances. These instances, called prototypes, just inherit the characteristics of other instances (called their constructor), which, in turn, is a prototype of another instance/constructor.

So let’s look at an illustrative view of what this really means:

This diagram shows a view of JavaScript’s prototype inheritance. Let’s start with Dog since that isn’t as abstract as Object or foo on either end of the chain. When the Dog object is created, it has a property called prototype; this property is just like any other property you might give to a dogs, such as numberOfLegs (another thing to adjust to when coming from Ruby is the convention of writing in camelcase!).

This property protoype of this instance of Animal called Dog lists all of the properties that the instance of Dog (e.g., a specific dog or maybe a type of dog, such as Bichon) will have once it is constructed. Now, once we create instances of Dog, otherwise known as prototypes of Dog, they will all have the properties that are defined in the prototype property (for example, if we say that when we construct an instance of Dog called Woofie, he should have all the properties and methods of Dog but also specific those of the prototype of dog, such as a name, “Woofie”).

Take a look at the diagram with the prototypes of Dog.

Now, when we look at the object of woofie the Dog, it will have a property called __proto__; this basically is a reference back to the prototype object that created it. So, two key things to remember:

  • The “`__proto__“` property is part of any object that is the prototype of another object (it was created using the prototype property of another object). This property holds the reference and points to the “`prototype“` that created it.
  • The “`prototype“` property is part of the object that is being used to construct the prototype. The information contained in this property is what is used as a template to create the prototype object (e.g., “`woofie“`). This property also holds a reference of “`constructor“` that points back to the object that created it. For example, the protoype of “`woofie“` (remember, the prototype of “`Dog“`) would hold a reference to “`Dog“` as its “`constructor“`.

Now, the object Dog was created the same way from the prototype of Animal as we created woofie from the Dog constructor. We can create an entire chain of prototypes in this way to create this prototype chain. At some point, we get to the instance of Object, which is the first object in Javascript. This means that it is not a prototype of anything - its __proto__ property holds a reference to null rather than to another prototype.

Enough talking, let’s look at some code! Let’s start by looking at one end of the chain at the object “foo”.

1
foo.constructor //this will result in Foo as you go up the chain

As you can see here, when you look at the constructor of the foo instance, you see that it is Foo since it was the prototype of Foo that created the instance of foo. Keep in mind that the constructor property here isn’t actually part of the foo object; it is part of the prototype object that the `__proto__ property of foo points to. However, as we mentioned, everything in Javascript is an instance of another object (except for Object which is not an instance of anything), so, you can call:

1
Foo.prototype.__proto__.constructor //this results in Bichon

In the example above, you basically traverse back to the object that created Foo by:

  1. Calling the prototype property of Foo
  2. Calling reference (“`__proto___“`) to the prototype that made Foo
  3. Looking at the constructor of that prototype, which is Bichon

Now, just for fun, lets take a look how we can traverse from the foo object to the Object object:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
foo.__proto__ //returns Foo.prototype

//both examples below return the same thing and are analagous to one another
Foo.prototype.__proto // returns the Bichon.prototype
foo.__proto__.__proto___ // returns the Bichon.prototype

//both examples below return the same thing and are analagous to one another
Bichon.prototype.__proto__ //returns Dog.prototype
foo.__proto__.__proto___.__proto___ //returns Dog.prototype

//both examples below return the same thing and are analagous to one another
Dog.prototype.__proto__ //returns Animal.prototype
foo.__proto__.__proto___.__proto___.__proto___ //returns Animal.prototype

//both examples below return the same thing and are analagous to one another
Animal.prototype.__proto__.constructor //returns Object
foo.__proto__.__proto___.__proto___.__proto___.__proto___.constructor //returns Object

For those who may be more visually inclined, here’s an illustration of this code that I put together of this traversal tht we’re doing:

Finally, let’s make this example a little more realistic and show how it relates to Javascript code. Let’s create a new Dog constructor:

1
2
3
4
5
6
7
8
function Dog(name){
  // This is a Dog constructor that will be used to create instances of a Dog, such as Woofie.
  // You use this space to put properties of the dog instance in here.
  this.name
  // REMEMBER: one of the properties of this constructor is the prototype object which holds a reference to the constructor and the
  // __proto__ that points to the prototype that created the Dog object.
  // The prototype is a property of this constructor JUST LIKE name is.
}

Now, let’s add some other behaviors the instances of Dog using the prototype property. You call this just like you would call any other property of Dog.

1
2
3
// Again, here, you are calling the prototype property just like you would any other property and then adding another property nested
// within that called "bark".
Dog.prototype.bark = function(){...}

Finally, let’s create Woofie! We use the Dog constructor to create Woofie.

1
var woofie = new Dog("Woofie");

Since Woofie has a __proto__ reference to the Dog prototype, it will also, naturally inherit the ability to bark that we gave all the prototypes of Dog.

Understanding the concepts above really helped me to better understand the fundamentals of Javascript. For further reading on this topic, check out these great resources:

Tinkering With the Internet of Things

I’ve always had a huge passion for things that connect our physical world to the online world. I’ve always tried to tinker with ways to make my lights turn on at home using my computer and I love products such as Nest, SmartThings and the Belkin WeMo. It was only just recently that I learned that this connection of our physical world to the digital world had a name - the Internet of Things!

According to Gartner, there will be nearly 26 billion devices on the Internet of Things by 2020. ABI Research estimates that more than 30 billion devices will be wirelessly connected to the Internet of Things (Internet of Everything) by 2020. So, I figured that this would be a good area to dig a little deeper on and tinker with.

My Experimentation with the Internet of Things

For my first foray into building an app that connects the physical world to the digital world, I wanted to build something simple - I decided to build a product that triggered an LED light to blink every time I received a new tweet.

For this, I needed an interface to connect to an LED light - I used an Arduino Uno microcontroller to do so.

Setting up the Arduino Uno

Arduino is an open-source electronics platform based on easy-to-use hardware and software. It’s intended for anyone making interactive projects. Arduino senses the environment by receiving inputs from many sensors, and affects its surroundings by controlling lights, motors, and other actuators. You can tell your Arduino what to do by writing code in the Arduino programming language, which apparently resembles Java.

Phase 1: Creating the Circuit for the LED light

For this phase, I built a very simple circuit. I connected an LED light with the anode in pin 13 (a pin which has an electrical current going through it, powered by the USB connection to the computer) and the cathode in the ground (to be a full circuit, you need a place for the electricity to go - the ground).

Phase 2: Sending Electrical Impulses Via Arduino Language

The next step was to then have this circuit board communicate with my computer so that I could have the computer generate on and off signals. This was done in the Arduino language: For us Rubyists used to human-readable code, this code can be a little bit daunting. Let’s break it down step by step:

  1. The first item, int ledPin = 13, is just setting a new variable called ledPin - we set it equal to the pin number where we plugged in the anode (positive end) of the light.
  2. The second part is one that is crucial to every Arduino based app - the setup. Here, we set up the baud rate to 9600. The baud rate is essentially the number of signals that will be sent per second. NOTE: when we get to building the ruby portion of this, it is crucial that the baud rates match up. In this setup section, we also define that pin 13 will be outputting a signal rather than receiving a signal. Finally, we set the initial state of the light to be off, or LOW.
  3. Next, we define the method for blinking the light. We set up a loop that runs 10 times (incrementing the variable i each time) and turns the light on and off every 1/10 of a second (based on the delays that we coded in).
  4. Next, we set up a method that defines WHEN the blinking begins. We set this up so that when it receives the string “b” on the serial port, it will trigger the blink_led method to fire off.
  5. Finally, the last method just checks to see if there is any signal at coming from the serial port. If there is, then to fire off the got_char method
Phase 3: Finally, time for some Ruby!

Now that we have the physical circuit created and we have the Arduino circuit ready to listen to any signal from our serial port and fire off blinking LEDs as a result, it is now time to build in the majority of our application logic.

First of all we have to connect our application to the serial port. The way we do this is by first installing and requiring the serialport gem. Then we instantiate the new serialport object:

1
2
3
4
5
6
7
8
9
require 'serialport'

baud_rate = 9600
port_file = "/dev/tty.usbmodem1411"
data_bits = 8
stop_bits = 1
parity = SerialPort::NONE

port = SerialPort.new(port_file, baud_rate, data_bits, stop_bits, parity)

As you can see here, the baud rate that we’ve set up matches up with the baud rate in the Arduino - this is what allows the two programs to communicate with one another. Next, we set up our port - this will be different for everyone - make sure that you find the USB serial port that the Arduino microcontroller is plugged into. This is all you need to set up the connection with Arduino!

Next, I worked on building in the Twitter API. Lucky, there’s a nice, handy Ruby Gem to make our lives easier. Using this gem, I created a new instance of an authenticated user based on the Twitter Developer consumer access keys and access tokens (I have removed my own credentials for security purposes).

1
2
3
4
5
6
7
8
require 'twitter'

client = Twitter::REST::Client.new do |config|
  config.consumer_key        = "YOUR_CONSUMER_KEY"
  config.consumer_secret     = "YOUR_CONSUMER_SECRET"
  config.access_token        = "YOUR_ACCESS_TOKEN"
  config.access_token_secret = "YOUR_ACCESS_SECRET"
end

Now that we have an instance of a client, there are a TON of methods that we can call on this client. The ones that we are interested in provide us with the latest activity on our timeline and on our mentions.

1
2
last_tweet = client.home_timeline.first
last_mention = client.mentions.first

Once we have pulled these values, we are now ready to write our last bit of code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
wait_time = 10

loop do

  check_tweet = client.home_timeline.first
  check_mention = client.mentions.first

  if last_tweet != check_tweet || last_mention != check_mention
    port.write "b"
  end

  last_tweet = check_tweet
  last_mention = check_mention

  sleep wait_time
end

Essentially, what this does is it goes into a loop forever where it checks the latest activity on the timeline and on the mentions again; it compares this with the initial value to see if it changed. If there was a change (i.e., there was a new post on your timeline), it sends the string “b” to the port (remember: this was the string that was the trigger for the LED lights).

One thing to note is that there is a wait_time incorporated between every iteration of the loop. One important thing to note is that the Twitter API (as well as most other APIs, presumably) limit the number of times you can send them a GET request to 180 calls every 15 minutes. The loop above is sending 2 GET requests every time it runs through the iteration, and since we are iterating every 10 seconds, we are sending 12 get requests every minute. Without spacing out the loop, you would get the following error very quickly and be locked out for 15 minutes. So, the entire Ruby code looked like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
require 'serialport'
require 'twitter'

client = Twitter::REST::Client.new do |config|
  config.consumer_key        = "YOUR_CONSUMER_KEY"
  config.consumer_secret     = "YOUR_CONSUMER_SECRET"
  config.access_token        = "YOUR_ACCESS_TOKEN"
  config.access_token_secret = "YOUR_ACCESS_SECRET"
end

last_tweet = client.home_timeline.first
last_mention = client.mentions.first

baud_rate = 9600
port_file = "/dev/tty.usbmodem1411"
data_bits = 8
stop_bits = 1
parity = SerialPort::NONE

port = SerialPort.new(port_file, baud_rate, data_bits, stop_bits, parity)

wait_time = 10

loop do

  check_tweet = client.home_timeline.first
  check_mention = client.mentions.first

  if last_tweet != check_tweet || last_mention != check_mention
    port.write "b"
  end

  last_tweet = check_tweet
  last_mention = check_mention

  sleep wait_time
end

Just a few final thoughts - though this application may seem somewhat trivial, the implications are enormous and exciting. Once you have the connection working between the logic that you create via code and an action with a physical object, the possibilities of what you can do are endless. It is not very hard to start connecting things such as motors, sensors, and buttons to the Arduino, and, with a little practice, it isn’t hard to pull in any API into your Ruby code.

Why We Create Objects in Ruby

When I first started learning how to code in Ruby, I kept hearing about how it was a purely object oriented language; however, I never REALLY understood what this meant on a practical level. So, this post is my attempt to try to clarify objects and object orientation for others who might be looking for a less abstract explanation.

What is an Object?

Before we discuss why we build objects, we should first understand and align on what objects are in the first place. It is easiest to understand this concept if you think of object in code as a model or representation of an object(any people, place or thing) in the real world.

For example, let’s say that you’re building an application that requires you to model a person (a tangible example of this if you want to create something like Siri for iPhones). In your code, you would create a new object called Person. The way to create such a Person object is to define a new Person class:

1
2
3
class Person

end

However, this would make a really boring person because we haven’t really defined any behaviors or things that this Person class can do! For simplicity’s sake, lets say that some of the key behaviors that all people can have are to say hello or to sneeze. We define these behaviors as methods within the body of the class:

1
2
3
4
5
6
7
8
9
class Person
  def say_hello
      puts "Hello, how are you doing today?"
  end

  def sneeze
      puts "Achooooo!!!"
  end
end

Now that we’ve done this, you can see that the Person object allows us to give people as a whole the ability to say hello and sneeze.

So, Why do we Create Objects?

To make our code better imitate reality so that it makes logical sense to us.

As you saw, the Person class we created makes sense to us (not as coders, but as humans, in general) - when you want to make the person (let’s use a person, Bob, as an example) say hello or sneeze, you just tell them to do so by passing the appropriate method: bob.say_hello or bob.sneeze. As you can imagine, we can then continue to build out our Person to infinite complexity by adding more such behaviors/methods.

Additionally, in the real world, objects are made up of other objects (e.g., people are made up of cells, countries are made up of people, sandwiches are made up of bread and meat) and share some characteristics with them.

Similarly, in Ruby, objects are also made of other objects and share many of the methods/behaviors with those objects.

Let’s take a look at an example in the illustration below:

As you can see, Ruby’s objects are designed in the same way as objects are in reality. You have BasicObject which serves as a “cell” or building block for all other objects that come out of it.

To see this more practically, you can see that if you pass class and superclass methods (these are methods that are shared across all objects in ruby) on any object in Ruby, at some point, you will bump into BasicObject! Try it out! You can do this on anything - whether it is an array, string, a number, a class, or hash, they will all lead up to BasicObject.

Illustration of how all Objects derive from BasicObject
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Person

end

bob = Person.new

# bob.class
#=> Person

# Person.class
# => Class 

# Person.class.superclass
# => Module 

# Person.class.superclass.superclass
# => Object 

# Person.class.superclass.superclass.superclass
# => BasicObject 

This is why everything in Ruby is an object! Everything is derived from the same building block of BasicObject. I never fully grasped this concept until I understood the Ruby object chain.

If you still don’t believe me - take a look at this somewhat more complex drawing.

This proves that EVERYTHING in Ruby is an object - even “nil” and all of the errors you get when coding!

Great, So Ruby Objects Model Real Life, How Does This Help Me?

Using objects simplifies our lives as coders. Again, let’s take a look at a real life example of a school. If we weren’t able to create objects such as a Students class where we can add the behavior that we want, we would have to constantly deal with more primitive data types, such as hashes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
school = {
:name => "High School",

:address => {:city => "New York, :state => NY"},

:teachers => [{:name => "Mr. Jones", :subject => "Math"},
      {:name => "Mrs. Brown", :subject => "English"},
      {:name => "Mr. King", :subject => "Gym"},
      {:name => "Mrs. Smith", :subject => "Science"}],
:students => [{:name => "Bobby", :grade => "9"},
      {:name => "Jimmy", :grade => "10"},
      {:name => "Steve", :grade => "11"},
      {:name => "Timmy", :grade => "11"},
      {:name => "Jenny", :grade => "12"}]
}

As you can imagine, this hash can get huge and insanely complicated. Imagine if we did really put all of the data for all of the teachers and all of the students in here. Now say we wanted to store 100 pieces of more pieces of data per student (e.g., address, class grades, classes taken). This mess of nested arrays and hashes would soon become very unwieldy if you had to pick out a certain student or compile data from this information. If you need to do this over and over again, it becomes even more cumbersome.

Instead, we can create a Student class/object or a Teachers class/object that responds to similar to how we created the person class.

Example of how we could create a Teacher object
1
2
3
4
5
6
7
8
9
10
class Teacher
  
  attr_reader :name, :subject

  def initialize (name, subject)
      @name = name
      @subject = subject
  end

end

As you can see in the example above, rather than navigating the complexities of the primitive hash every time we want to get information about a teacher, all we have to do is something like the following to get information about a teacher:

Much simpler than navigating through the nested hash and arrays, right?
1
2
3
4
5
6
7
8
9
10
11
mrjones = Teacher.new("Mr. Jones", "Math")

# Now that we have intitialized Mr. Jones as an object in 
# the line above, we can just pass the methods "subject" 
# and "name" to Mr. Jones

mrjones.subject
# => "Math"

mrjones.name
# => "Mr. Jones"

Creating objects such as these allow us to just “set it and forget it.” Rather than having to comb through a primitive objects such as hashes over and over again, we can just use a quicker, and more natural way of getting the same result. Our object now becomes a black box - once you create it and it behaves as you like, you never have to worry about the internal workings of that object again unless you want to change functionality.

I hope that this was a good primer on objects - all of the topics discussed here really helped me to understand what object orientation really means in Ruby.