The code in this repository wants to teach you a little bit about the magic of the Ruby programming language, in particular, Ruby objects, using inspiration from Harry Potter.
It was created for RailsGirls.London, December 2018, and revised for Book Machine’s Talking Tech, May 2019, onto the Glitch platform. It does contain mild spoilers.
This Glitch project has everything you need to play with this code in your browser, without needing to download anything on to your computer. But, as you'll discover with all code projects, some preparation is required. Think of the following steps like a seven small obstacles stopping you from getting to the Philospher's Stone.
You should be reading this from the Glitch project summary page (the name of the project in the browser's url will start with a `~`). If you are not on that page, click on this link to start from there and avoid confusion.
Start by clicking the Remix This button, at the bottom right of the screen, so that you can play with the code.
In the remixing or editing view, you could see three sections, the file list on the far left, the README file open in the middle and this page in the Show section on the right. If you don't see three sections, find the "Show" button near the top and choose the "Next to the Code" button. You want your browser to look like this:
Next, in order to interact with the code, to control the characters, you need to have an an environment that can execute code (the console). Glitch provides a console in two ways: as a full screen or underneath an open code file. I recommend using the console underneath the code, so you can follow the instructions at the same time.
To reveal the console, click the "Tools" button found at the bottom of the file list, then select "Logs". At the top of the window that appears, click the “Console” button. Now your browser should look like this:
You now have the ability to read code in the middle section, scroll through the instructions in the right hand section and type code commands in the bottom console window. These are all the tools that a developer uses, so you are well on your way to making your own magic.
The console is set to execute directory-level commands, not Ruby commands. We need to tell it to open a Ruby-friendly environment. The Ruby programming language provides its own environment called IRB (interactive Ruby). My friend @AndyPearson has done a bit of behind-the-scenes magic in the irb.rc
file to set up IRB in a particular way, to make it easier for you to focus on learning the object-ness of Ruby. So now, all you have to do is:
Start IRB by typing the three letters: irb
into the console after the $
character, then hit return.
You’ll see a prompt that looks like irb(main):001:0>
which means you are now in a world where you can type Ruby commands and are ready to begin at Hogwarts School of Learning a Bit of Ruby.
(Imagine that I am channeling Professor McGonagall. And be grateful that you don’t have to hear my awful impersonation.)
Today, children, you are going to transform some bits of code into a wizard who can cast spells.
How do you make Harry Potter as code? In Ruby, he must be an object. A Ruby object is a bundle of code that has attributes and behaviours; an object can be and do things as a single unit. An Object, with a capital O.
Ruby expects you to code your own classes that will create objects to your specification.
You could create all the things in the Harry Potter universe - the people, the places, the wands. And you’d be able to make them do the things they do in the stories. Basically, you’d be J K Rowling.
But we’re just going to start with Harry.
Naming is hard but, to get started, I have named the class that creates Harry Potter, the Character class.
In the file list on the far left, click on the file called model/character.rb
. This is the code that defines the Character class.
I have already defined the Character class with an attribute called name
and a behaviour called speak
.
But this code isn’t Harry Potter. This is the code that will create Harry Potter. We have to instruct the Character class to create him. This is how you do that:
In your IRB console type: @harry = Character.new(name: "Harry Potter")
and hit return.
The @harry is "storing" the Character that you create, so that we can access it many times during the exercise. In Ruby, you don't always need to put an @ sign in front of your variable names like we have here with @harry. We use the @ in these examples to make the code reloadable, otherwise you'd need to keep restarting your console as you make changes to your code.
You should see output like: => #<Character:0x007fcb9283d050 @name="Harry Potter">
We know Harry can speak, because he was created by a class that has the defined behaviour speak
. Let’s make him speak.
Type: @harry.speak("Hello")
and hit return.
You should see the output:
Hello.
=> nil
Fantastic! We have Harry Potter and he can speak.
There are other characters in the story. So, using the same Character class, let’s make another, but with different parameters:
@aunt_petunia = Character.new(name: "Petunia Dursely")
She can speak, too, but if you don’t have something nice to say…
That’s enough characters to get us started.
Character
)We have Harry but, right now, he can only speak. We’ve only defined that one behaviour for Character. Where’s his magic?
Let’s add it in.
In the file models/character.rb and, just above the end
on line 12, add the following code (I recommend you type in out and don't copy and paste, so it can havea bit of time to sink into your brain):
def cast(spell)
puts spell.upcase + "!"
end
accio_code
and hit return. This will "reload the magic", allowing you to use your new code.Now type: harry.cast("Expecto patronum")
You’ll see the capitalised magic spell output:
> EXPECTO PATRONUM!
This is truly magic! But wait!! Repello Muggletum! We have a problem!
aunt_petunia.cast("Expecto patronum")
and hit return.No, no, no! Aunt Petunia shouldn’t be able to cast a spell! What would J K Rowling say?
How can we make our two character objects behave the same in some ways but differently in others? How can code object Harry do magic, while code object Petunia cannot?
With the code we have now, that is not possible. Because both objects are created from the class Character, either both objects get magic, or neither.
We can’t keep the code that casts spells in the Character class. We have to extract the spell method and keep it firmly in the magic world, behind a big brick wall that Muggles can’t get through.
Go have a peek in concerns/magic.rb. You’ll see the cast method there:
def cast(spell)
puts spell.upcase + "!"
end
Notice that this doesn’t say class Magic
at the top. It says module Magic
. A module is Ruby’s way of being able to make some behaviours that can be shared with objects. A module doesn’t create a new object, like a Class does. It is for holding behaviours.
Separating out the magic is only the first step. We have to sort our characters into different types of characters.
One is a Wizard, and one is most definitely a Muggle (complicated backstory not withstanding). However, both types of characters have a name and both can speak, which should remain coded into the class Character.
We need to keep both Harry and Petunia as characters, but ensure only the Wizard can do magic.
Look at the file models/wizard.rb
.
You’ll see:
class Wizard < Character
include Magic
end
And models/muggle.rb
gives us:
class Muggle < Character
end
The less-than sign before Character means that the class Wizard is a type of Character, and inherits all the traits and behaviour of Character.
But only the Wizard class gets Magic included.
Now, we reload IRB again and create our objects again, using new classes:
@harry = Wizard.new(name: "Harry Potter")
@aunt_petunia = Muggle.new(name: "Petunia Dursely")
And while Harry can cast his spell: @harry.cast("Expecto patronum")
> EXPECTO PATRONUM!
Aunt Petunia cannot aunt_petunia.cast("Expecto patronum")
NoMethodError: undefined method 'cast' for #<Muggle:0x007ffc72009738 @name="Petunia Dursely">
from (irb):6
from /usr/bin/irb:11:in '<main>'
The error message says that Aunt Petunia doesn’t know how to cast – undefined method.
Of course, there is more than one type of magic user. Harry uses a wand to make his magic, but Dobby doesn’t need a wand to do his magic.
Ruby magic lets us create a HouseElf class that also inherits from Character and include the Magic module. Now, we are sharing the behaviours of Magic across different object classes.
I haven’t done that for you – I challenge to you to do it for yourself. Make a HouseElf class, give it magic and then create a Dobby!
In conclusion, there is no magic spell for learning how to code quickly. But with each little piece of code that you make do what you tell it to, you’ll build up your powers to make something as rich and beautiful, and hopefully as successful as the Harry Potter world.
And, if you're looking for a support system while learning to code, check out #SideProjectSummer, where publishing people are gathering on Twitter every Friday afternoon to do parcel-sized code learning.
Good luck!