Classes in Ruby

Ruby is what's called an "object-oriented" programming language, but what does that mean? Is the code there to manipulate real life objects? Is there a sort of virtual Lego set in your computer for building little robots? Sort of. Object-oriented programming lets you model the structure of your overall program on smaller components made of code, which can learn and recall attributes specific to their identities. So instead of just writing code that performs something like "dog barks now", you can have code that actually represents the dog, and have that dog know how to bark. Moreover, using something called a "class", the dog will know how to bark because all objects in the class "Dog" know how to bark. Sound useful? Maybe not yet, but this can give a programmer lots of power in the environments they create.

Let's say you find yourself face-to-face with a Terminator. This is a killing machine. It will decide whether to take you out based on its programming, or more specifically, what functions it executes based on its mission, its target, its environment etc. If you were a post-apocalyptic AI charged with designing a robot to go back in time and kill Sarah Connor, how would you do it? Would you just start by making your T-800 walk until it finds her? How does it know when it's in danger of failing its mission, or coming to harm itself? Your code would quickly become sprawling and unorganized - and maintenance would be a nightmare.

Worse, what if you were future John Connor, and you needed to re-program a captured T-800? Before you send that thing back in time you better make absolutely sure you found every bit of code that directs it to "murder Sarah Connor"! You'd like to tell it to do something like "please don't murder Sarah Connor". Every terminator has a target, right? Yours used to be "Sarah Connor", and you want to change it to "T-1000". How does it know what to do with that information?

The AI who designed the terminator has already built in a target definition - in fact it's built in to every terminator. The T-800 you captured isn't even the same one that tried to kill your mom in the 80's, it just looks the same. So the T-800 model, like all terminators, is humanoid, can speak, has a target that it hunts etc. - but this particular one has its own unique target. This makes it so much easier to crank out all those fake Arnolds at the end of "Terminator: Salvation" - the software is all set up, you just have to give specific parameters to each instance of your terminator and ship it off!

In Ruby this set of guiding functionalities is called a class. All the terminators know how to cook a fool - that's what they do - they get the blueprint for this from their class: "Terminator". Let's look at a simple Terminator class:


          class Terminator

            def terminate
              puts "Hasta la vista, baby."
              puts "(You're totally terminated)"
            end
          end
        

Within our definition for the Terminator class, we have a method to terminate. We can't do anything with this until we have an object of class Terminator. So let's make one!


          johnny_five = Terminator.new
          johnny_five.terminate
          => Hasta la vista, baby.
          => (You're totally terminated)
        

johnny_five is an "instance" of an object within the Terminator class. This means johnny_five inherits all the capabilities defined by Terminator and can call on those using "dot notation" like: johnny_five.terminate. What about those specific individual traits that aren't necessarily shared with every terminator? You can define those as arguments when you create a new instance of the class:


          schwartz = Terminator.new("T-800", "Sarah Connor", "Arnold-ness", 8)
        

Each of those items in the parentheses correspond to a parameter that the object internalized when it "initializes":


          def initialize(model, target, skill, scary_scale)
            @model = model
            @target = target
            @skill = skill
            @scary_scale = scary_scale
          end
        

Now this new terminator, schwartz, "knows" that its model is "T-800", its target is "Sarah Connor", its skill is "Arnold-ness"(?), and its scary_scale is 8. If we like, now we can give the Terminator class a new method:


          def identify
            puts "I am a #{@model} terminator sent from the future. I have been programmed to
            terminate #{@target} using #{@skill}"
          end
        

So we can inquire of this charming new fellow:


          schwartz.identify
          => I am a T-800 terminator sent from the future. I have been programmed to
          terminate Sarah Connor using Arnold-ness
        

Lovely. All those @ symbols before our variables have special meaning for our object. These demark "instance variables", meaning they are accessible within the definition of the class, and can be called upon by our object (schwartz) for its "instance methods". These are just methods that can be called by our instance. So identify is an instance method, which can access instance variables @model, @target and @skill to have custom functionality for our instance.

What about that last instance variable, @scary_scale? Just for fun, we can use this as a ranking for a short survival game. Here's the instance method we can call:


          def how_dead_are_you
            if @scary_scale > 8
              puts "You're toast, don't even think about trying to save the future."
              terminate
            elsif @scary_scale > 4 && @scary_scale <= 8
              puts "Action sequence!"
              death_chance = rand(10)
              sleep(1)
              if death_chance + @scary_scale > 9
                puts "You've failed, Skynet goes live."
                terminate
              else
                puts "You've escaped ... until the sequel."
              end
            else
              puts "This must be the T-X model, from Terminator 3. Feel free to take a nap."
            end
          end
        

So it looks like the higher we set @scary_scale the less chance we make it out alive because terminate is called.

I hope this post has been just instructive enough to get you comfortable with how classes work in Ruby, but not instructive enough to allow you to build your own terminator to send back in time and prevent me from writing this. Hasta la vista.

Don't insult the t-one thousand's cooking