Class it up!
Tags: student, ruby, class, method
Published: 2020-09-25 12:56:58

Class is a neat little word. Among all of its uses in the English language, they all in some way refer to a categorization of things. Socio-economic *class*, vehicle *class*, biological taxonomy *class*. It's a very versatile word, and its meaning and traits carry into Ruby! But just like the any bit of clerical organization, building a class takes a little work. It's worth it, however, because Object-Oriented programming in Ruby loves to use classes to help you build cool and efficient code. 

To start, you build a beachhead with this syntax:


    class Monster
    end


Bam! There's a class. But it is super empty and wants to be filled with all sorts of juicy data nuggets, or Objects. Like most things in programming, Objects need to be explicitly defined. So up there we have a class Monster, because obviously, you're a Dungeon Master preparing your next session of the world's great roleplaying game<sub><sup><sub><sup><sub><sup><sub><sup><sub><sup><sub><sup><sub><sup><sub><sup>tm</sup></sub></sup></sub></sup></sub></sup></sub></sup></sub></sup></sub></sup></sub></sup></sub>. And what's a good adventure without a few bag guys for your intrepid heroes to get into a scrum with?

So we want to come up with a way to have a few monsters and their pertinent information. For starters, every monster needs a name, so we can go ahead and figure that we'll need one of those.


    class Monster

        attr_accessor :name

        def initialize(name)
            @name = name
        end

    end

Whooaaa, Bessie, you might be thinking. What's with the giddyup? That's a lot to just get a name of a lil' twerp goblin. Don't worry, intrepid reader, it all has a purpose.

What he have set up is a few things. The line 'attr_accessor :name' is a dual-purpose tool that you can use the set up both a *writer* and a *reader* for the name of an Instance of the class. This way, you will be able to both change and call on the information that is the 'name' of any given Instance of `class Monster`. There is specific syntax you can use to *only* write or read, but for this, we want to do both. Because if your players adopt a goblin, as they invariably will, you need to be able to give 'goblin8' a new, more charming name, like '"Sparx"'.

Below the accessor we have method `initialize`, which currently only takes the argument of a name. Within the method, you have a single line of code, '@name = name'. @name is a local variable within the class Monster. What this initialize method is doing is allowing us to assign a name to **every** Instance of Monster without having to write out a separate method for each one. Isn't that sweet!

Now, if you create a new Instance of Monster below the class with the line 'goblin = Monster.new("goblin")', you get your first little Object nugget!


    => #<Monster:0x00007fe2b09b0150 @name="goblin">

But that's just the name of the goblin. You can't run an encounter without any stats! So let's beef up our class a little bit with a few more details.

    class Monster

        attr_accessor :name, :weapon, :ability

        def initialize(name, weapon, ability)
            @name = name
            @weapon = weapon
            @ability = ability
        end

    end

Now we have a cool spread of information to fill your Monster Instances with. If you run this through your code...

    goblin = Monster.new("goblin", "shortbow", "nimble escape")

Whoa, what an upgrade. But man, one goblin solo isn't going to do much good for you. You should send it out with a few friends. Goblins can run with a few different friends, often times bugbears or hobgoblins!

    bugbear = Monster.new("bugbear", "morning star", "surprise attack")
    hobgoblin = Monster.new("hobgoblin", "longsword", "leadership")

Here's what this dastardly trio looks like in Instance form:

    goblin
    => #<Monster:0x00007ff42310fda0 @ability="nimble escape", @name="goblin", @weapon="shortbow">
    bugbear
    => #<Monster:0x00007ff42310fd00 @ability="surprise attack", @name="bugbear", @weapon="morning star">
    hobgoblin
    => #<Monster:0x00007ff42310fc60 @ability="leadership", @name="hobgoblin", @weapon="longsword">

Now it's a party! You have your sneaky goblin, your brutal bugbear bruiser, and the stern hobgoblin to lead them against your player's intrepid heroes. But how to gather them all up? Very simple!

    class Monster

        attr_accessor :name, :weapon, :ability

        @@all = []

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

    end

See what we added? '@@all' is what we call a class variable. It starts out equaling an empty array. If you follow the syntax, you'll see that the method initialize will push every new Instance of Monster into this array as it comes into existence. And then with a single method outside the initialize:

    def self.all
        @@all
    end

You can summon the monsters from the dark wood, ready to do your bidding! Now, in any future methods you create for this class, you can use this self method to call on all of its Instances! This is a good way to reference the information in this class without a lot of bloat in your code.

The initialize method and the class variable are two steps you can take at the start of building your class that can make a lot of your legwork down the way more streamlined and refined.