Ruby Class and Module

Ruby Class and Module

Ruby Class and Module

A class is a blueprint for objects. You have only one class called Shape, but with it you can create multiple instances of shapes (Shapeobjects), all of which have the methods and attributes defined by the Shape class. An object is an instance of a class. If Shape is the class, then x = Shape.new creates a new Shape instance and makes x reference that object. You would then say x is a Shape object, or an object of class Shape.

 

Local, Global, Object, and Class Variables

A simple demonstration of a class with two methods, and how to use it. First, here’s the class itself:

class Square
def initialize(side_length)
@side_length = side_length
end
def area
@side_length * @side_length
end
end
Next, let’s create some square objects and use their area methods:
a = Square.new(10)
b = Square.new(5)
puts a.area
puts b.area
100
25

 

The first method—and when I say “first,” I mean the first method in our example; the actual order of methods in code is irrelevant—in the Square class is initialize. initialize is a special method that’s called when a new object based on that class is created. When you call Square.new(10), the Square class creates a new object instance of itself, and then calls initializeon that object.

 

In this case, initialize accepts a single argument into side_length as passed by Square.new(10), and assigns the number 10 (now referenced by side_length) to a variable called @side_length. The @ symbol before the variable name is important in this case. But why? To understand why some variables are prefixed with certain symbols requires understanding that there are multiple types of variables, such as local, global, object, and class variables.

 

Local Variables

 

Local Variables

In previous examples you’ve created variables simply, like so:

  • x = 10
  • puts x
  • 10

In Ruby, this sort of basic variable is called a local variable. It can be used only in the same place it is defined. If you jump to using an object’s methods or a separate method of your own, the variable x doesn’t come with you. It’s considered to be local in scope. That is, it’s only present within the local area of code. Here’s an example that demonstrates this:

def  basic_method

puts  x

end

x  =  10

basic_method

This example defines x to equal 10, and then jumps to a local method called basic_method. If you ran this code through irb, you would get an error like this:

 

NameError: undefined local variable or method 'x' for main:Object

What’s happening is that when you jump to basic_method, you’re no longer in the same scope as the variable x that you created. Because x is a local variable, it exists only where it was defined. To avoid this problem, it’s important to remember to use only local variables where they’re being directly used.

 

Here’s an example where you have two local variables with the same name but in different scopes:

>def basic_method
x = 50
puts x
end
x = 10
basic_method
puts x
50
10

 

This demonstrates that local variables live entirely in their original scope. You set x to 10 in the main code, and set x to 50 inside the method, but x is still 10 when you return to the original scope. The xvariable inside basic_methodis not the same xvariable that’s outside of the method. They’re separate variables, distinct within their own scopes.

 

Global Variables

Global Variables

In opposition to local variables, Ruby can also use global variables. As their name suggests, global variables are available from everywhere within an application, including inside classes or objects.

 

Global variables can be useful, but aren’t commonly used in Ruby. They don’t mesh well with the ideals of object-oriented programming, as once you start using global variables across an application, your code is likely to become dependent on them. Because the ability to separate blocks of logic from one another is a useful aspect of object-oriented programming, global variables are not favored.

 

You define global variables by putting a dollar sign ($) in front of the variable name, like so:

def basic_method
puts $x
end
$x = 10
basic_method
10

 

$x is defined as a global variable, and you can use it anywhere in your application.

Note The $and @characters that denote global variables and object variables (as demonstrated in the next section) are technically called sigils. Many developers are, however, unaware of this. This blog is not beyond giving you knowledge that can make you more popular at cocktail parties!

 

Instance or Object Variables

Where local variables are specific to the local scope, and global variables have global scope, instance variables (also known as object variables) are so named because they have scope within, and are associated with, the current object. A demonstration of this concept was shown at the start of this section with the Square class:

class Square
def initialize(side_length)
@side_length = side_length
end
def area
@side_length * @side_length
end
end

 

Instance variables are prefixed with an @ symbol. In the Square class, you assign the side_length provided to the class to @side_length. @side_length, as an instance variable, is then accessible from any other method inside that object. That’s how the area method can then use @side_length to calculate the area of the square represented by the object:

a = Square.new(10)
b = Square.new(5)
puts a.area
puts b.area
100
25

The results are different, even though the code to work out the area in both cases is @side_length

@side_length. This is because @side_length is an instance variable associated only with the current object or instance.

 

Tip If you didn’t fully understand the Shape/Square/Triangleexample at the start of this blog, now would be a good time to look back at it, as it used several object variables to develop its functionality.

 

Class Variables

Class Variables

The last major type of variable is the class variable. The scope of a class variable is within the class itself, as opposed to within specific objects of that class. Class variables start with two @ symbols (@@) as opposed to the single @ symbol of instance variables.

 

Class variables can be useful for storing information relevant to all objects of a certain class. For example, you could store the number of objects created so far in a certain class using a class variable like so:

class Square
def initialize
if defined?(@@number_of_squares)
@@number_of_squares += 1
else
@@number_of_squares = 1
end
end
def self.count
@@number_of_squares
end
end
a = Square.new
b = Square.new
puts Square.count
2

Because @@number_of_squares is a class variable, it’s already defined each time you create a new object.

Note In recent years, class variables have begun to fall out of favor among professional Ruby developers. Fashions come and go in the Ruby world but ultimately enable developers to work together more smoothly. Since all classes are themselves objects within Ruby, it has become more popular to simply use object variables within the context of class methods in order to keep things simple.

 

Class Methods versus Instance Methods

In your Square class you defined two methods: initialize and area. Both are instance methods, as they relate to, and operate directly on, an instance of an object. Here’s the code again:

class Square
def initialize(side_length)
@side_length = side_length
end
def area
@side_length * @side_length
end
end

Once you’ve created a square with s = Square.new(10), you can use s.area to get back the area of the square represented by s. The area method is made available in all objects of class Square, so it’s considered to be an instance method.

 

However, methods are not just useful to have available on object instances. It can be useful to have methods that work directly on the class itself. In the previous section you used a class variable to keep a count of how many square objects had been created, and it would be useful to access the @@number_of_squares class variable in some way other than through Square objects.

 

Here’s a simple demonstration of a class method:

class Square
def self.test_method
puts "Hello from the Square class!"
end
def test_method
puts "Hello from an instance of class Square!"
end
Square.test_method
Square.new.test_method
Hello from the Square class!
Hello from an instance of class Square!

 

This class has two methods. The first is a class method, and the second is an instance method, although both have the same name of test_method. The difference is that the class method is denoted with self., where self represents the current class, so def self.test_method defines the method as being specific to the class. However, with no prefix, methods are automatically instance methods.

 

Alternatively, you could define the method like so:

class  Square

def  self.test_method

puts  "Hello  from  the  Square  class!"

end

end

 

The style you use (ClassName.method_name versus self.method_name) comes down to personal preference. Using self.method_name (as in self.test_method) doesn’t require you to restate the class name over and over, but ClassName.method_name (as in Square.test_method) is a closer match to what you’ll be using to call that method later on.

 

Note Throughout the rest of this blog, I’ll mostly use the self.method_namestyle, but you can use whichever style you like in your own code. The main disadvantage of using the class name rather than selfis that using selfmakes it easier to rename the class later on without having to update the names of all your class methods.

 

Class methods give you the mechanism to properly implement the “object counter” hinted at earlier:

class Square
def initialize
if defined?(@@number_of_squares)
@@number_of_squares += 1
else
@@number_of_squares = 1
end
end
def self.count
@@number_of_squares
end
end
Let’s give it a try:
a = Square.new
puts Square.count
b = Square.new
puts Square.count
c = Square.new
puts Square.count
1
2
3

Notice you don’t refer to a, b, or c at all to get the count. You just use the Square.count class method directly. Consider it as if you’re asking the class to do something that’s relevant to the class as a whole, rather than asking the objects.

 

Inheritance

Inheritance

An interesting object-oriented programming concept is inheritance, which allows you to generate a taxonomy of classes and objects. If you consider all living things as a class called LivingThing, under that class you could have (and let’s keep this simple, biologists!) Plant and Animal classes.

 

Under Animal you’d have Mammal, Fish, Amphibian, and so forth. Digging into Mammal, you could work through Primate and Human. A Human is a living thing, a Human is an Animal, a Human is a Mammal, and so forth, but each level down is more specific and targeted than that above it. This is class inheritance in action! The same system applied to the Shape example where Triangle and Square inherited directly from Shape.

 

The benefit of inheritance is that classes lower down the hierarchy get the features of those higher up, but can also add specific features of their own.

 

The basic “all living things” class is so generic that the only functionality you could give to it is a basic “living or dead” method. However, at the animal level, you could add methods such as eat, excrete, or breathe. At the human level you’d inherit all this functionality but be able to add human methods and qualities such as sing, dance, and love.

 

Ruby’s inheritance features are similarly simple. Any class can inherit the features and functionality of another class, but a class can inherit only from a single other class. Some other languages support multiple inheritance, a feature that allows classes to inherit features from multiple classes, but Ruby doesn’t support this.

 

Multiple inheritance can cause some confusing situations—for instance, classes could inherit from one another in an endless loop—and the efficacy of multiple inheritance is debatable.

 

Let’s look at how inheritance works in code form:

class ParentClass
def method1
puts "Hello from method1 in the parent class"
end
def method2
puts "Hello from method2 in the parent class"
end
end
class ChildClass < ParentClass
def method2
puts "Hello from method2 in the child class"
end
end
my_object = ChildClass.new
my_object.method1
Hello from method1 in the parent class
my_object.method2
Hello from method2 in the child class

First you create the ParentClass with two methods, method1 and method2. Then you create ChildClass and make it inherit from ParentClass using the ChildClass<ParentClass notation. Last, you create an object instance of ChildClass and call its method1 and method2 methods.

 

The first result demonstrates inheritance perfectly. ChildClass has no method1 of its own, but because it has inherited from ParentClass, and ParentClass has a method1, it uses it.

 

However, in the second case, ChildClass already has a method2 method, so the method2 method supplied by the parent class is ignored. In many cases, this is ideal behavior, as it allows your more specific classes to override behavior provided by more general classes. However, in some situations you might want a child method to call an inherited method and do something with the result.

 

Consider some basic classes that represent different types of people:

class Person
def initialize(name)
@name = name
end
def name
return @name
end
end
class Doctor < Person
def name
"Dr. " + super
end
end

In this case you have a Person class that implements the basic functionality of storing and returning a person’s name. The Doctor class inherits from Person and overrides the name method. Within the name method for doctors, it returns a string starting with Dr., appended with the name as usual.

 

This occurs by using super, which looks up the inheritance chain and calls the method of the same name on the next highest class. In this example, you only have two tiers, so using super within the name method in Doctor then uses the name method in Person.

 

The benefit of using inheritance in this way is that you can implement generic functionality in generic classes, and then implement only the specific functionality that more specific child classes require. This saves a lot of repetition and means that if you make changes to the parent classes, child classes will inherit these changes too.

 

A good example of this might be if you changed Person to take two arguments, firstname and lastname. The Doctor class wouldn’t need to be changed at all to support this change. With one child class this doesn’t seem too important, but when you have hundreds of different classes in an application, it pays to cut down on repetition!

 

Note In the Ruby world, the concept of cutting down on repetition is commonly called DRY, meaning Don’t Repeat Yourself. If you can code something once and reuse it from multiple places, that’s usually the best way to practice “DRY”.

 

Overriding Existing Methods

Overriding Existing Methods

Because it’s a dynamic language, one clever thing you can do with Ruby is override existing classes and methods. For example, consider Ruby’s String class. if you create a string, you end up with an object of class String; for example:

x = "This is a test"

puts x.class

 

String

You can call a number of different methods upon the String object stored in x:

puts x.length
puts x.upcase
14
THIS IS A TEST
Let’s stir things up a bit by overriding the length method of the String class:
class String
def length
20
end
end

 

Many newcomers to Ruby, even experienced developers, initially fail to believe this will work, but the results are exactly as the code dictates:

puts "This is a test".length
puts "a".length
puts "A really long line of text".length
20
20
20

 

Some libraries and extensions (add-ons) to Ruby override the methods supplied by the core classes to extend the functionality of Ruby in general. However, this demonstration shows why it’s always necessary to tread with caution and be aware of what’s going on in your application. If you were relying on being able to measure the length of strings, and the length method gets overridden, you’re going to have a hard time!

 

You should also note that you can override your own methods. In fact, you’ve probably been doing it a lot already by following these examples in irb:

class Dog
def talk
puts "Woof!"
end
end
my_dog = Dog.new
my_dog.talk
Woof!
class Dog
def talk
puts "Howl!"
end
end
my_dog.talk
Howl!

In this example, you created a basic class with a simple method, then reopened that class and redefined a method on the fly. The results of the redefinition were made effective immediately, and my_dog began to howl as a result.

 

This ability to reopen classes and add and redefine methods is relatively unique among object-oriented languages. Although it allows you to perform a number of interesting tricks (some of which you’ll see in action later), it can also cause the same sections of code to act in different ways depending on whether certain classes upon which you depend were changed in the application, as demonstrated by your redefinition of String’s length method previously.

 

Note You might have noticed this class-reopening technique in action in some of our earlier examples where you created methods in one example, only to add new methods in a later example. If running under irb or within the same program, reopening a class lets you add new methods or change old ones without losing anything.

 

Another interesting method is instance_variables. It returns the names of any object variables associated with an instance (as opposed to class variables):

class Person
attr_accessor :name, :age
end
p = Person.new
p.name = "Fred"
p.age = 20
puts p.instance_variables
@age
@name

At this stage you might not see the value in these reflective methods, but as you progress toward becoming more proficient with Ruby and object orientation, they’ll become more important. 

 

Encapsulation

 Encapsulation

Encapsulation describes the way in which data and methods can be bundled together into objects that operate as a single unit. Within Ruby, however, the term is often used to describe the ability for an object to have certain methods and attributes available for use publicly (from any section of code), but for others to be visible only within the class itself or by other objects of the same class.

 

The rationale for encapsulation is that some developers believe you should make as few methods as possible available from your classes, so that even if you choose to rewrite the internals of your classes, you can maintain a few methods that interface between other elements of your system and your classes and objects.

 

Encapsulation helps you keep a lot of functionality within your classes, but gives you the security of only having a few ways for the outside world to manipulate your object’s data (thus maintaining the overall “single unit” of data and code bound together). This can allow you to extend and change your classes without the worry that other elements of your application will break.

 

Here’s an example class that represents a person:

class Person
def initialize(name)
set_name(name)
end
def name
@first_name + ' ' + @last_name
end
def set_name(name)
first_name, last_name = name.split(/\s+/)
set_first_name(first_name)
set_last_name(last_name)
end
def set_first_name(name)
@first_name = name
end
def set_last_name(name)
@last_name = name
end
end

 

In previous examples, you would have written this with a single attr_accessor :name and simply assigned the name to an object variable. Unfortunately, though, real-life constraints often require a different approach.

 

In this case, the first name and last name are stored separately within each Person object, in object variables called @first_name and @last_name. When a Person object is created, the name is split into two halves and each is assigned to the correct object variable by set_first_name and set_last_name, respectively.

 

One possible reason for such a construction could be that although you want to work with complete names in your application, the database design might demand you have first names and last names in separate columns. Therefore, you need to hide this difference by handling it in the class code, as in the preceding code.

 

Note A side benefit of this approach is that you can perform checks on the data before assigning it to the object variables. For example, in the set_first_nameand set_last_namemethods, you could check that the names contain enough characters to be considered valid names. If not, you can then raise an error.

The code appears to work fine:
p = Person.new("Fred Bloggs")
puts p.name
Fred Bloggs
However, you still seem to have some problems:
p = Person.new("Fred Bloggs")
p.set_last_name("Smith")
puts p.name
Fred Smith

 

Uh-oh! You wanted to abstract the first name/last name requirement and only allow full names to be set or retrieved. However, the set_first_name and set_last_name are still public and you can use them directly from any code where you have Person objects. Luckily, encapsulation lets you solve the problem:

class Person
def initialize(name)
set_name(name)
end
def name
@first_name + ' ' + @last_name
end
private
def set_name(name)
first_name, last_name = name.split(/\s+/)
set_first_name(first_name)
set_last_name(last_name)
end
def set_first_name(name)
@first_name = name
end
def set_last_name(name)
@last_name = name
end
end

 

The only difference in the Person class from the first time you created it is that the keyword private has been added. private tells Ruby that any methods declared in this class from there on should be kept private. This means that only code within the object’s methods can access those private methods, whereas code outside of the class cannot. For example, this code no longer works:

p  =  Person.new("Fred  Bloggs")

p.set_last_name("Smith")


private method 'set_last_name' called for #<Person:0x337b68 @last_name="Bloggs", @first_name="Fred">

 

The opposite of the private keyword is public. You could put private before one method, but then revert to public methods again afterwards using public, like so:

class Person
def anyone_can_access_this
...
end
private
def this_is_private
...
end
public
def another_public_method
...
end
end
You can also use private as a command by passing in symbols representing the methods you want to keep private, like so:
class Person
def anyone_can_access_this; ...; end
def this_is_private; ...; end
def this_is_also_private; ...; end
def another_public_method; ...; end
private :this_is_private, :this_is_also_private end

Note Ruby supports ending lines of code with semicolons (;) and allows you to put multiple lines of code onto a single line (for example, x = 10; x += 1; putsx). In this case, it’s been done to save on lines of code in the example, although it’s not considered good style in production-quality Ruby code.

 

The command tells Ruby that this_is_private and this_is_also_private are to be made into private methods. Whether you choose to use private as a directive before methods or as a command specifying the method names directly is up to you, and is another of many technically unimportant stylistic decisions you’ll make as a Ruby programmer.

 

However, it’s important to note that in the preceding example, the private declaration has to come after the methods are defined.

Ruby supports a third form of encapsulation (other than public and private) called protected that makes a method private, but within the scope of a class rather than within a single object.

For example, you were unable to directly call a private method outside the scope of that object and its methods. However, you can call a protected method from the scope of the methods of any object that’s a member of the same class:

 

class Person
def initialize(age)
@age = age
end
def age
@age
end
def age_difference_with(other_person)
(self.age - other_person.age).abs
end
protected :age
end
fred = Person.new(34)
chris = Person.new(25)
puts chris.age_difference_with(fred)
puts chris.age
9
protected method 'age' called for #<Person:0x1e5f28 @age=25> (NoMethodError)

 

The preceding example uses a protected method so that the age method cannot be used directly, except within any method belonging to an object of the Person class. However, if age were made private, the preceding example would fail because other_person.age would be invalid. That’s because private makes methods accessible only by methods of a specific object.

 

Note that when you use age directly, on the last line, Ruby throws an exception.

 

Note Some Ruby developers do not believe marking methods as private or protected provides any significant value and that, indeed, it prevents developers from having free and open access to their code. We’re not going to push a particular opinion here, but you may wish to avoid using these features until you have a strong opinion either way.

 

Polymorphism

Polymorphism

Polymorphism is the concept of writing code that can work with objects of multiple types and classes at once. For example, the + method works for adding numbers, joining strings, and adding arrays together. What + does depends entirely on what type of things you’re adding together.

Here’s a Ruby interpretation of a common demonstration of polymorphism:

class Animal
attr_accessor :name
def initialize(name)
@name = name
end
end
class Cat < Animal
def talk
"Meaow!"
end
end
class Dog < Animal
def talk
"Woof!"
end
end
animals = [Cat.new("Flossie"), Dog.new("Clive"), Cat.new("Max")] animals.each do |animal|
puts animal.talk
end
Meaow!
Woof!
Meaow!

 

In this example, you define three classes: an Animal class, and Dog and Cat classes that inherit from Animal. In the code at the bottom, you create an array of various animal objects: two Cat objects and a Dog object (whose names are all processed by the generic initialize method from the Animal class).

 

Next, you iterate over each of the animals, and on each loop you place the animal object into the local variable, animal. Last, you run puts animal.talk for each animal in turn. As the talk method is defined on both the Cat and Dog class, but with different output, you get the correct output of two “Meaow!”s and two “Woof!”s.

 

This demonstration shows how you can loop over and work on objects of different classes, but get the expected results in each case if each class implements the same methods.

If you were to create new classes under the Cat or Dog classes with inheritance (for example, class Labrador < Dog), then Labrador.new.talk would still return “Woof!” thanks to inheritance.

 

Some of Ruby’s built-in standard classes (such as Array, Hash, String, and so on) have polymorphic methods of their own. For example, you can call the to_s method on many built-in classes to return the contents of the object as a string:

puts  1000.to_s

puts  [1,2,3].to_s

puts  ({  :name  =>  'Fred',  :age  =>  10  }).to_s


1000

[1,2,3]

{:name  =>  ‘Fred’,  :age  =>  10}

The output isn’t particularly useful in this case, but being able to rely on most objects to return a string with to_s can come in useful in many situations, such as when putting representations of objects into strings.

 

Nested Classes

Nested Classes

In Ruby, it’s possible to define classes within other classes. These are called nested classes. Nested classes are useful when a class depends on other classes, but those classes aren’t necessarily useful anywhere else. They can also be useful when you want to separate classes into groups of classes rather than keep them all distinct. Here’s an example:

class  Drawing

class  Line

end

class  Circle

end

end

Nested classes are defined in the same way as usual. However, they’re used differently.

From within Drawing, you can access the Line and Circle classes directly, but from outside the Drawing class, you can only access Line and Circle as Drawing::Line and Drawing::Circle. For example:

class Drawing
def self.give_me_a_circle
Circle.new
end
class Line
end
class Circle
def what_am_i
"This is a circle"
end
end
end
a = Drawing.give_me_a_circle
puts a.what_am_i
a = Drawing::Circle.new
puts a.what_am_i
a = Circle.new
puts a.what_am_i
This is a circle
This is a circle
NameError: uninitialized constant Circle

= Drawing.give_me_a_circle calls the give_me_a_circle class method, which returns a new instance of Drawing::Circle. Next, a = Drawing::Circle.new gets a new instance of Drawing::Circle directly, which also works. The third attempt, a = Circle.new, does not work, however, because Circle doesn’t exist. That’s because as a nested class under Drawing, it’s known as Drawing::Circle instead.

You’re going to use nested classes in a project at the end of this blog, where you’ll see how they work in the scope of an entire program.

 

Modules, Namespaces, and Mix-Ins

Namespaces

Modules provide a structure to collect Ruby classes, methods, and constants into a single, separately named and defined unit. This is useful so you can avoid clashes with existing classes, methods, and constants, and also so that you can add (mix in) the functionality of modules into your classes. First, we’ll look at how to use modules to create namespaces to avoid name-related clashes.

 

Namespaces

One common feature used in Ruby is the ability to include code situated in other files into the current program. When including other files, you can quickly run into conflicts, particularly if files or libraries you’re including then include multiple files of their own. You cannot guarantee that no file that’s included (or one that’s included in a long chain of includes) will clash with code you’ve already written or processed. Take this example:

  • def random
  • rand(1000000)
  • end
  • puts random

 

The random method returns a random number between 0 and 999,999. This method could be in a remote file where it’s easily forgotten, which would cause problems if you had another file you included using require that implemented a method like so:

def random

(rand(26) + 65).chr

end

This random method returns a random capital letter.

Note (rand(26) + 65).chrgenerates a random number between 0 and 25 and adds 65 to it, giving a number in the range of 65 to 90. The chrmethod then converts a number into a character using the ASCII standard where 65 is A, through to 90, which is Z. You can learn more about the ASCII character set at http://en.wikipedia.org/wiki/ASCII.

 

Now you have two methods called random. If the first random method is in a file called number_stuff.rb and the second random method is in a file called letter_stuff.rb, you’re going to hit problems:

require './number_stuff'

require './letter_stuff'

puts random

 

Which version of the random method is called?

Note requireis a Ruby statement used to load in code contained within another file.

 

As the last file loaded, it turns out to be the latter version of random, and a random letter should appear onscreen. Unfortunately, however, it means your other random method has been “lost.”

 

This situation is known as a name conflict, and it can happen in even more gruesome situations than the simplistic example shown in the preceding code. For example, class names can clash similarly, and you could end up with two classes mixed into one by accident.

 

If a class called Song is defined in one external file and then defined in a second external file, the class Song available in your program will be a dirty mix of the two. Sometimes this might be the intended behavior, but in other cases this can cause significant problems.Modules  help to solve these conflicts by providing namespaces that can contain any number of classes, methods, and constants, and allow you to address them directly. For example:

 

module NumberStuff
def self.random
rand(1000000)
end
end
module LetterStuff
def self.random
(rand(26) + 65).chr
end
end
puts NumberStuff.random
puts LetterStuff.random
184783
X

Note Due to the randomness introduced by using rand, the results will vary every time you run the program!

In this demonstration it’s clear which version of random you’re trying to use in the two last lines. The modules defined in the preceding code look a little like classes, except they’re defined with the word module instead of class. However, in reality you cannot define instances of a module, as they’re not actually classes, nor can they inherit from anything. Modules simply provide ways to organize methods, classes, and constants into separate namespaces.

 

A more complex example could involve demonstrating two classes with the same name, but in different modules:

module ToolBox
class Ruler
attr_accessor :length
end
end
module Country
class Ruler
attr_accessor :name
end
end
a = ToolBox::Ruler.new
a.length = 50
b = Country::Ruler.new
b.name = "Genghis Khan from Moskau"

Rather than having the Ruler classes fighting it out for supremacy, or ending up with a mutant Ruler class with both name and length attributes (how many measuring rulers have names?), the Ruler classes are kept separately in the ToolBox and Country namespaces. You’ll be looking at why namespaces are even more useful than this later, but first you have to look at the second reason why modules are so useful.

 

Mix-Ins

Ruby’s inheritance

Earlier you studied inheritance: the feature of object orientation that allows classes (and their instance objects) to inherit methods from other classes. You discovered that Ruby doesn’t support multiple inheritance, the ability to inherit from multiple classes at the same time. Instead, Ruby’s inheritance functionality only lets you create simple trees of classes, avoiding the confusion inherent with multiple inheritance systems.

 

However, in some cases it can be useful to share functionality between disparate classes. In this sense, modules act like a sort of bundle of methods, classes, and constants that can be included into other classes, extending that class with the methods the module offers. For example:

module UsefulFeatures
def class_name
self.class.to_s
end
end
class Person
include UsefulFeatures
end
x = Person.new
puts x.class_name
Person

In this code, UsefulFeatures looks almost like a class and, well, it almost is. However, modules are organizational tools rather than classes themselves. The class_name method exists within the module and is then included in the Person class. Here’s another example:

module AnotherModule
def do_stuff
puts "This is a test"
end
end
include AnotherModule
do_stuff
This is a test

As you can see, you can include module methods in the current scope, even if you’re not directly within a class. Somewhat like a class, though, you can use the methods directly:

AnotherModule.do_stuff

 

Therefore, include takes a module and includes its contents into the current scope.

Ruby comes with several modules by standard that you can use. For example, the Kernel module contains all the “standard” commands you use in Ruby (such as load, require, exit, puts, and eval) without getting involved with objects or classes.

 

None of those methods are taking place directly in the scope of an object (as with the methods in your own programs), but they’re special methods that get included in all classes (including the main scope), by default, through the Kernel module. However, of more interest to us are the modules Ruby provides that you can include in your own classes to gain more functionality immediately. Two such modules are Enumerable and Comparable.

 

Enumerable

Enumerable

In previous blogs you’ve performed the process of iteration, like so:

[1,2,3,4,5].each { |number| puts number }

In this case, you create a temporary array containing the numbers 1 through 5 and use the each iterator to pass each value into the code block, assigning each value to number that you then print to the screen with puts.

 

The each iterator gives you a lot of power, as it allows you to go through all the elements of an array or a hash and use the data you retrieve to work out, for example, the mean of an array of numbers, or the length of the longest string in an array, like so:

my_array = %w{this is a test of the longest word check} longest_word = ''

my_array.each  do  |word|

longest_word = word if longest_word.length < word.length end

puts  longest_word


longest

 

In this case, you loop through my_array, and if the currently stored longest word is shorter than the length of word, you assign it to longest_word. When the loop finishes, the longest word is in longest_word. The same code could be tweaked to find the largest (or smallest) number in a set of numbers:

>my_array = %w{10 56 92 3 49 588 18}
highest_number = 0
my_array.each do |number|
number = number.to_i
highest_number = number if number > highest_number end
puts highest_number
588

However, the Array class (for one) has pre-included the methods provided by the Enumerable module, a module that supplies about 20 useful countingand iteration-related methods, including collect, detect, find, find_all, include?, max, min, select, sort,and to_a.

 

All of these use Array’s each method to do their jobs, and if your class can implement an each method, you can include Enumerable, and get all those methods for free in your own class!

First, some examples of the methods provided by Enumerable:

[1,2,3,4].collect { |i| i.to_s + "x" }
=> ["1x", "2x", "3x", "4x"]
[1,2,3,4].detect { |i| i.between?(2,3) }
=> 2
[1,2,3,4].select { |i| i.between?(2,3) }
=> [2,3]
[4,1,3,2].sort
=> [1,2,3,4]
[1,2,3,4].max
=> 4
[1,2,3,4].min
=> 1
You can make your own class, implement an each method, and get these methods for “free”:
class AllVowels
VOWELS = %w{a e i o u}
def each
VOWELS.each { |v| yield v }
end
end

This is a class that, in reality, doesn’t need to provide multiple objects, as it only provides an enumeration of vowels. However, to keep the demonstration simple, it is ideal. Here’s how it works:

x = AllVowels.new
x.each { |v| puts v }
a
e
i
o
u

Your AllVowels class contains an array constant containing the vowels, and the instance-level each method iterates through the array constant VOWELS and yields to the code block supplied to each, passing in each vowel, using yield v. Let’s get Enumerable involved:

class AllVowels
include Enumerable
VOWELS = %w{a e i o u}
def each
VOWELS.each { |v| yield v }
end
end
Now let’s try to use those methods provided by Enumerable again. First let’s get an AllVowels object:
x = AllVowels.new
Now you can call the methods on x:
x.collect { |i| i + "x" }
=> ["ax", "ex", "ix", "ox", "ux"]
x.detect { |i| i > "j" }
=> "o"
x.select { |i| i > "j" }
=> ["o", "u"]
x.sort
=> ["a", "e", "i", "o", "u"]
x.max
=> "u"
x.min
=> "a"

 

Comparable

Comparable

The Comparable module provides methods that give other classes comparison operators such as < (less than), <= (less than or equal to), == (equal to), >= (greater than or equal to), and > (greater than), as well as the between? method that returns true if the value is between (inclusively) the two parameters supplied (for example, 4.between?(3,10) == true).

 

To provide these methods, the Comparable module uses the <=> comparison operator on the class that includes it. <=> returns -1 if the supplied parameter is higher than the object’s value, 0 if they are equal, or 1 if the object’s value is higher than the parameter. For example:

1 <=> 2
-1
1 <=> 1
0
2 <=> 1
1

With this simple method, the Comparable module can provide the other basic comparison operators and between?. Create your own class to try it out:

class Song
include Comparable
attr_accessor :length
def <=>(other)
@length <=> other.length
end
def initialize(song_name, length)
@song_name = song_name
@length = length
end
end
a = Song.new('Rock around the clock', 143)
b = Song.new('Bohemian Rhapsody', 544)
c = Song.new('Minute Waltz', 60)
Here are the results of including the Comparable module:
a < b
=> true
b >= c
=> true
c > a
=> false
a.between?(c,b)
=> true

You can compare the songs as if you’re comparing numbers. Technically, you are. By implementing the <=> method on the Song class, individual song objects can be compared directly, and you use their lengths to do so. You could have implemented <=> to compare by the length of the song title, or any other attribute, if you wished.

 

Modules give you the same ability to implement similar generic sets of functionality that you can then apply to arbitrary classes. For example, you could create a module that implements longest and shortest methods that could be included into Array, Hash,or other classes, and returns the longest or shortest string in a list.

 

Using Mix-Ins with Namespaces and Classes

In a previous example, I demonstrated how you can use modules to define namespaces using the following code:

module ToolBox
class Ruler
attr_accessor :length
end
end
module Country
class Ruler
attr_accessor :name
end
end
a = ToolBox::Ruler.new
a.length = 50
b = Country::Ruler.new
b.name = "Genghis Khan of Moskau"

In this case, the Ruler classes were accessed by directly addressing them via their respective modules (as ToolBox::Ruler and Country::Ruler).

However, what if you wanted to assume temporarily that Ruler (with no module name prefixed) was Country::Ruler, and that if you wanted to access any other Ruler class, you’d refer to it directly? include makes it possible.

 

In the previous sections, you’ve used include to include the methods of a module in the current class and scope, but it also includes the classes present within a module (if any) and makes them locally accessible too. Say, after the prior code, you did this:

include Country

c = Ruler.new

http://c.name = "King Henry VIII"

 

Success! The Country module’s contents (in this case, just the Ruler class) are brought into the current scope, and you can use Ruler as if it’s a local class. If you want to use the Ruler class located under ToolBox, you can still refer to it directly as ToolBox::Ruler.

 

Building a Dungeon Text Adventure with Objects

So far in this blog you’ve looked at object-oriented concepts in depth, mostly in a technical sense. At this point, it would be useful to extend that knowledge by applying it in a real-world scenario.

 

In this section, you’re going to implement a mini text adventure/virtual dungeon. Text adventures were popular in the 1980s, but have fallen out of favor with modern gamers seeking graphical thrills. They’re perfect playgrounds for experimenting with classes and objects, though, as replicating the real world in a virtual form requires a complete understanding of mapping real-world concepts into classes.

 

Dungeon Concepts

Dungeon Concepts

Before you can develop your classes, you have to figure out what you’re trying to model. Your dungeon isn’t going to be complex at all, but you’ll design it to cope with at least the following concepts:

 

Dungeon: You need a general class that encapsulates the entire concept of the dungeon game.

Player: The player provides the link between the dungeon and you. All experience of the dungeon comes through the player. The player can move between rooms in the dungeon.

 

Rooms: The rooms of the dungeon are the locations that the player can navigate between. These will be linked together in multiple ways (doors to the north, west, east, and south, for example) and have descriptions.

A complete adventure would also have concepts representing items, enemies, other characters, waypoints, spells, and triggers for various puzzles and outcomes. You could easily extend what you’ll develop into a more complete game later on if you want.

 

Creating the Initial Classes

Creating the Initial Classes

Our first concept to develop is that of the dungeon and the game itself. Within this framework come the other concepts, such as the player and rooms.

 

Using nested classes, you can lay down the initial code like so:

class Dungeon
attr_accessor :player
def initialize(player)
@player = player
@rooms = {}
end
end
class Player
attr_accessor :name, :location
def initialize(player_name)
@name = player_name
end
end
class Room
attr_accessor :reference, :name, :description, :connections
def initialize(reference, name, description, connections)
@reference = reference
@name = name
@description = description
@connections = connections
end
end

 

This code lays down the framework for your dungeon.

Your dungeon currently has instance variables to store the player (since the player may change the state of the dungeon in some way) and the list of rooms (@rooms = {} creates an empty Hash; it’s equivalent to @rooms = Hash.new).

 

The Player class lets the player object keep track of his or her name and current location. The Room class lets room objects store their name, description (for example, “Torture Chamber” and “This is a dark, foreboding room.”), and connections to other rooms, as well as a reference (to be used by other rooms for their connections).

 

When you create a dungeon with Dungeon.new, it expects to receive the name of the player, whereupon it creates that player and assigns it to the dungeon’s object variable @player. This is because the player and the dungeon need to be linked, so storing the player object within the dungeon object makes sense. You can easily access the player because the player variable has been made into an accessor with attr_accessor. For example:

me  =  Player.new("Fred  Bloggs")

my_dungeon  =  Dungeon.new(me)

puts  my_dungeon.player.name
Fred  Bloggs

You can access the player functionality directly by going through the dungeon object. As @player contains the player object, and as @player has been made publicly accessible with attr_accessor :player, you get complete access.

 

Structs: Quick and Easy Data Classes

One thing should stand out about the main code listing so far. It’s repetitive. The Room and Player classes are merely acting as basic placeholders for data rather than as true classes with logic and functionality. There’s an easier way to create this sort of special data-holding class in Ruby with a single line of a class called a struct.

 

A struct is a special class whose only job is to have attributes and to hold data. Here’s a demonstration:

Person = Struct.new(:name, :gender, :age)
fred = Person.new("Fred", "male", 50)
chris = Person.new("Chris", "male", 25)
puts fred.age + chris.age
75

 

Simply, the Struct class builds classes to store data. On the first line you create a new class called Person that has built-in name, gender, and age attributes. On the second line you create a new object instance of Person and set the attributes on the fly. The first line is equivalent to this longhand method:

class Person
attr_accessor :name, :gender, :age
def initialize(name, gender, age)
@name = name
@gender = gender
@age = age
end
end

Note In actuality, this code is not exactly equivalent to the struct code (though pragmatically it’s close enough), because parameters are optional when initializing a Structclass, whereas the preceding Personclass code requires the three parameters (name, gender, and age) be present.

 

This code creates a Person class the “long way.” If all you want to do is store some data, then the struct technique is quicker to type and easier to read, although if you ultimately want to add more functionality to the class, creating a class the long way is worth the effort. However, the good thing is that you can start out with a struct and recode it into a full class when you’re ready. This is what you’re going to do with your dungeon. Let’s rewrite it from scratch:

 class Dungeon
attr_accessor :player
def initialize(player)
@player = player
@rooms = {}
end
end
Player = Struct.new(:name, :location)
Room = Struct.new(:reference, :name, :description, :connections)

It’s certainly shorter, and because parameters are optional when creating instances of Struct classes, you can still use Player.new(player_name), and the location attribute is merely set to nil. If you ever need to add methods to Player or Room, you can rewrite them as classes and add the attributes back with attr_accessor.

 

attr_accessor

Throughout the code in this blog, you have used attr_accessor within classes to provide attributes for your objects. Recall that attr_accessor allows you to do this:

class Person
attr_accessor :name, :age
end
x = Person.new
x.name = "Fred"
x.age = 10
puts x.name, x.age
However, in reality attr_accessor isn’t doing anything magical. It’s simply writing some code for you.
This code is equivalent to the single attr_accessor :name, :age line in the preceding Person class:
class Person
def name
@name
end
def name=(name)
@name = name
end
def age
@age
end
def age=(age)
@age = age
end
end

 

This code defines the name and age methods that return the current object variables for those attributes, so that http://x.name and x.age (as in the prior code) work. It also defines two “setter” methods that assign the values to the @name and @age object variables.

 

If you pay attention to the names of the setter methods, you’ll see they’re the same as the methods that return values but suffixed with an equals sign (=). This means they’re the methods that are run for code such as http://x.name = "Fred" and x.age = 10. In Ruby, assignments are just calls to regular methods! Indeed, http://x.name = "Fred" is merely shorthand for http://x.name=("Fred").

 

Creating Rooms

Creating Rooms

Your dungeon now has the basic classes in place, but there’s still no way to create rooms, so let’s add a method to the Dungeon class:

class Dungeon
def add_room(reference, name, description, connections)
@rooms[reference] = Room.new(reference, name, description, connections) end
end
You want to add rooms to the dungeon, so adding a method to dungeon objects makes the most sense.
Now you can create rooms like so (if my_dungeon is still defined, of course):
my_dungeon.add_room(:largecave, "Large Cave", "a large cavernous cave", { « :west => :smallcave })
my_dungeon.add_room(:smallcave, "Small Cave", "a small, claustrophobic cave", { « :east => :largecave })

add_room accepts the reference, name, description, and connections arguments and creates a new Room object with them before adding that object to the @rooms hash.

 

The reference, name, and descriptions arguments should seem obvious, but the connections argument is designed to accept a hash that represents the connections that a particular room has with other rooms.

 

For example, { :west => :smallcave } ties two symbols (:west and :smallcave) together. Your dungeon logic uses this link to connect the rooms. A connections hash of { :west => :smallcave, :south => :another_room } would create two connections (one to the west heading to “small cave”, and one to the south heading to “another room”).

 

Making the Dungeon Work

Making the Dungeon Work

You have all the rooms loaded for your basic dungeon (and can add more whenever you like with the add_ room method), but you have no way of navigating the dungeon itself.

The first step is to create a method within Dungeon that starts everything off by placing the user into the dungeon and giving you the description of the initial location:

class Dungeon
def start(location)
@player.location = location
show_current_description
end
def show_current_description
puts find_room_in_dungeon(@player.location).full_description end
def find_room_in_dungeon(reference)
@rooms[reference]
end
end
class Room
def full_description
@name + "\n\nYou are in " + @description
end
end

 

You define a start method within the dungeon that sets the player’s location attribute. It then calls the dungeon’s show_current_description method, which finds the room based on the player’s location, and then prints the full description of that location to the screen.

 

full_description does the work of taking the location’s name and description and turning it into a full, useful description. find_room_in_dungeon, on the other hand, returns the room whose reference matches that of the current location.

 

However, the problem with the preceding code is that Room is a struct, rather than a full class, so it becomes necessary to turn it into a full class once again (as hinted at earlier). This change requires a few key changes, so to keep things simple, here’s the complete code so far, along with the change of Room to a regular class and some additional methods to aid navigation of the dungeon:

class Dungeon
attr_accessor :player
def initialize(player)
@player = player
@rooms = {}
end
def add_room(reference, name, description, connections)
@rooms[reference] = Room.new(reference, name, description, connections)
end
def start(location)
@player.location = location
show_current_description
end
def show_current_description
puts find_room_in_dungeon(@player.location).full_description end
def find_room_in_dungeon(reference)
@rooms[reference]
end
def find_room_in_direction(direction)
find_room_in_dungeon(@player.location).connections[direction]
end
def go(direction)
puts "You go " + direction.to_s
@player.location = find_room_in_direction(direction)
show_current_description
end
end
class Player
attr_accessor :name, :location
def initialize(name)
@name = name
end
end
class Room
attr_accessor :reference, :name, :description, :connections
def initialize(reference, name, description, connections)
@reference = reference
@name = name
@description = description
@connections = connections
end
def full_description
@name + "\n\nYou are in " + @description
end
end
player = Player.new("Fred Bloggs")
my_dungeon = Dungeon.new(player)
# Add rooms to the dungeon
my_dungeon.add_room(:largecave,
"Large Cave",
"a large cavernous cave",
{ :west => :smallcave })
my_dungeon.add_room(:smallcave,
"Small Cave",
"a small, claustrophobic cave",
{ :east => :largecave })
Start the dungeon by placing the player in the large cave my_dungeon.start(:largecave)
Large Cave
You are in a large cavernous cave

 

It’s a long piece of source code, but most of it should make sense by now. You’ve changed Room and Player into true classes once more, and implemented the basics of the dungeon.

 

Two particularly interesting methods have been added to the Dungeon class:

def find_room_in_direction(direction)
find_room_in_dungeon(@player.location).connections[direction]
end
def go(direction)
puts "You go " + direction.to_s
@player.location = find_room_in_direction(direction)
show_current_description
end

The go method is what makes navigating the dungeon possible. It takes a single argument—the direction to travel in—and uses that to change the player’s location to the room that’s in that direction.

It does this by calling find_room_in_direction, a method that takes the reference related to the relevant direction’s connection on the current room, and returns the reference of the destination room. Remember that you define a room like so:

>my_dungeon.add_room(:largecave,
"Large Cave",
"a large cavernous cave",
{ :west => :smallcave })

 

If :largecave is the current room, then find_room_in_direction(:west) will use the connections on that room to return :smallcave, and this is then assigned to @player.location to define that as the new current location.

 

To test the navigation of the dungeon, you can simply type thego commands if you’re using irb, or if you’re working with a source file in an editor, you’ll need to add the go commands to the end of your source code and re-run it. Here’s what happens:

my_dungeon.show_current_description
Large Cave
You are in a large cavernous cave
my_dungeon.go(:west)
You go west
Small Cave
You are in a small, claustrophobic cave
my_dungeon.go(:east)
You go east
Large Cave
You are in a large cavernous cave

The code has no error checking (try going to a nonexistent room with my_dungeon. go(:south)), and lacks items, an inventory, and other basic text-adventure features, but you now have an operational group of objects that represents a dungeon, and that can be navigated in a basic fashion.

This code is rife for extension and manipulation. With another class and several more methods you could easily add support for items within the game that you can place at different locations, pick up, and then drop at other locations.

 

If you want an exercise, you can try turning the preceding dungeon code into a truly interactive program by creating a loop that uses the gets method to retrieve instructions from the player and then to “go” wherever the player determines. You can use chomp to strip off the newline characters from the incoming text, and to_sym to convert strings into symbols for the go method.

 

This might seem like a tough task at this stage, but if you pull it off I guarantee you’ll have learned a lot and you’ll be confident about going on to the next blog.

 

Projects and Libraries

 

Projects and Libraries

In previous blogs we’ve looked at and worked with Ruby from a low-level perspective by working directly with classes, objects, and functions. Each line of code we’ve used in the small projects so far has been written specifically for that project from scratch. In this blog, we’ll look at how to build larger projects with Ruby, and how to reuse code written previously.

 

Finally, we’ll look at how to use code already written and prepared by other developers within your own applications so that you don’t need to reinvent the wheel every time you create a new program.

This blog is about the bigger picture: dealing with projects and libraries.

 

Projects and Using Code from Other Files

 

Ruby Module

As you become more familiar with Ruby and find more uses for it, it’s likely that you’ll want to move from writing single small programs (with fewer than 100 or so lines) to more complex applications and systems made up of multiple parts. Larger applications and systems therefore often become known as projects, and are managed in a different way than simple one-file scripts.

 

The most common way to separate functionality in Ruby is to put different classes in different files. This gives you the ability to write classes that could be used in multiple projects simply by copying the file into your other project.

 

Basic File Inclusion

Consider this code: puts "This is a test".vowels.join('-')

If you try to execute this code, you’ll get an error complaining that the vowels method is not available for the "This is a test" object of class String. This is true because Ruby doesn’t provide that method. Let’s write an extension to the String class to provide it:

  • class String
  • def vowels
  • self.scan(/[aeiou]/i)
  • end
  • end

If this definition were included in the same file as the prior puts code—say, my_test.rb—the result would be as follows: i-i-a-e

In this case, you’ve extended String with a vowels method that uses scan to return an array of all the vowels (the i option on the end makes the regular expression case-insensitive).

 

However, you might want to write a number of methods to add to String that you’d like to use in multiple programs. Rather than copy and paste the code each time, you can copy it to a separate file and use the require command to load the external file into the current program. For example, put this code in a file called string_extensions.rb:

>class String
def vowels
self.scan(/[aeiou]/i)
end
end
And put this code in a file called vowel_test.rb:
require './string_extensions'
puts "This is a test".vowels.join('-')

 

If you run vowel_test.rb, the expected result would appear onscreen. The first line, require './ string_extensions', simply loads in the string_extensions.rb file from the current directory (as signified by the ./) and processes it as if the code were local. This means that, in this case, the vowels method is available, all with a single line.

 

Ruby does not include the current directory in the path of directories to search for Ruby files by default, so you can either specify the current directory specifically by using ./, as above, or by using require_relative. So this example is operationally identical to the previous one:

require_relative 'string_extensions'

puts "This is a test".vowels.join('-')

 

As well as require and require_relative, you can use load to load external source code files into your program. For example, this code would seem to function identically to the preceding code:

load 'string_extensions.rb'

puts "This is a test".vowels.join('-')

Noteload requires a full filename, including the .rb suffix, whereas require assumes the .rb suffix.

 

The output is the same in this case, but let’s try a different example to see the difference. Put this in a .rb:

puts "Hello from a.rb"
And put this in a file called b.rb:
require_relative 'a'
puts "Hello from b.rb"
require_relative 'a'
puts "Hello again from b.rb"
Run with ruby b.rb to get the result:
Hello from a.rb
Hello rom b.rb
Hello again from b.rb

 

In this example, the a.rb file is included only once. It’s included on line 1, and "Hello from a.rb" gets printed to the screen, but then when it’s included again on line 3 of b.rb, nothing occurs. In contrast, consider this code:

load 'a.rb'
puts "Hello from b.rb"
load 'a.rb'
puts "Hello again from b.rb"
Hello from a.rb
Hello from b.rb
Hello from a.rb
Hello again from b.rb

With load, the code is loaded and reprocessed anew each time you use the load method. require and require_relative, on the other hand, process external files only once.

 

Note Ruby programmers nearly always use require or require_relative rather than load. The effects of load are useful only if the code in the external file has changed or if it contains active code that will be executed immediately. However, a good programmer will avoid the latter situation, and external files will only contain classes and modules that will, generally, rarely change.

 

Inclusions from Other Directories

Inclusions from Other Directories

load and require have different approaches to finding files to load. load and require_relative can bring in local files, but require does not. require 'a' looks for a.rb in a multitude of other directories on your storage drive. By default, these other directories are the various directories where Ruby stores its own files and libraries, although you can override this, when necessary.

 

Ruby stores the list of directories to search for included files in a special variable called $: (or, if you prefer,  $LOAD_PATH). You can see what $: contains by default, using irb:

$:.each { |d| puts d }
/usr/local/lib/ruby/site_ruby/2.2.0
/usr/local/lib/ruby/site_ruby/2.2.0/i686-darwin8.8.1
/usr/local/lib/ruby/site_ruby
/usr/local/lib/ruby/2.2.0
/usr/local/lib/ruby/2.2.0/i686-darwin8.8.1

 

Note This result is what appears on my machine but the list of directories will probably differ significantly on your machine, particularly if you’re using Windows, where the path layout will be entirely different, with the drive letter at the start and backslashes instead of forward slashes.

 

If you want to add directories to this, it’s simple:

$:.push '/your/directory/here'

require 'yourfile'

$: is an array, so you can push extra items to it or use unshift to add an element to the start of the list (if you want your directory to be searched before the default Ruby ones—useful if you want to override Ruby’s standard libraries).

 

Logic and Including Code

Logic and Including Code

require and load both act like normal code in Ruby programs. You can put them at any point in your Ruby code and they’ll behave as if they were processed at that point. For example:

$debug_mode = 0

require_relative $debug_mode == 0 ? "normal-classes" : "debug-classes"

 

It’s an obscure example, but it checks if the global variable $debug_mode is set to 0. If it is, it requires normal-classes.rb, and if not, debug-classes.rb. This gives you the power to include a different source file dependent on the value of a variable, ideal for situations where your application has “regular” and “debug” modes.

 

You could even write an application that works perfectly, but then use a different require to include a whole different set of files that have new or experimental functionality.

 

A commonly used shortcut uses arrays to quickly load a collection of libraries at once. For example:

%w{file1 file2 file3 file4 file5}.each { |l| require l }

This loads five different external files or libraries with just two lines of code. However, some coders are not keen on this style, as it can make the code harder to read, even if it’s more efficient.

 

Nested Inclusions

Nested Inclusions

Code from files that are included in others with require and load has the same freedom as if the code were pasted directly into the original file. This means files that you include can call load, require, or require_relative themselves. For example, assume a.rb contains the following:

require_relative 'b'
and b.rb contains the following:
require_relative 'c'
and c.rb contains the following:
def example
puts "Hello!"
end
and d.rb contains the following:
require_relative 'a'
example
When d.rb is then run,
Hello!

d.rb includes a.rb with require, a.rb includes b.rb, and b.rb includes c.rb, meaning the example method is available to d.rb. This functionality makes it easy to put together large projects with interdependent parts, as the structure can be as deep as you like.

 

Libraries

Libraries

In computer programming, a library is a collection of routines that can be called by separate programs but that exist independently of those programs. For example, you could create a library to load and process a data file, and then use the routines in that library from any number of other programs.

 

Earlier in this blog we looked at using the require command to load external files into your Ruby program we looked at how modules can be used to separate elements of functionality into separate namespaces. You can use both of these concepts, jointly, to make libraries in Ruby.

 

At the start of this blog you developed an extremely simple library called string_extensions.rb, like so:

class String
def vowels
self.scan(/[aeiou]/i)
end
end
And you used this library with the following code:
require 'string_extensions'
puts "This is a test".vowels.join('-')
i-i-a-e

Nearly all libraries are more complex than this simple example, but nonetheless, THIS is a basic demonstration of how a library works.

 

Next we’re going to look at the libraries that come standard with Ruby, and look at a way to download and use libraries that other developers have made available on the Internet.

 

The Standard Libraries

Ruby comes with many standard libraries. They provide Ruby with a wide selection of functionality “out of the box,” from webserving and networking tools through to encryption, benchmarking, and testing routines.

 

Note Collectively the “standard libraries” are often called “the Standard Library.” When you see this term, it’s important to remember it most likely refers to the collection rather than one library in particular—a “library of libraries,” if you will.

 

In this section we’re going to look at how you can use just two random standard libraries (net/http and OpenStruct), so that you’re prepared for using and working with other libraries in later blogs, where you’ll be using many other standard libraries in a similar way. The choice of these two libraries is reasonably arbitrary, although both are commonly used by Rubyists whereas some of the standard libraries get little use at all.

 

A list of all the standard libraries, including documentation, is available at Documenting the Ruby Language stdlib/

 

Note Some users might discover that the number of standard libraries might have been trimmed down, particularly if using a preinstalled version of Ruby. However, if you installed Ruby from source, all the demonstrations in this section should work.

 

net/http

 

HyperText Transfer Protocol

HTTP stands for HyperText Transfer Protocol, and it’s the main protocol that makes the World Wide Web work, as it provides the mechanism by which web pages, files, and other media can be sent between Web servers and clients.

 

Ruby provides basic support for HTTP via the net/http library. For example, it’s trivial to write a Ruby script that can download and print out the contents of a particular web page:

require 'net/http'

Net::HTTP.get_print('Ruby Inside: The Ruby Blog', '/')

 

If you ran this code, after a few seconds many pages of HTML code should fly past on your screen. The first line loads the net/http library into the current program, and the second line calls a class method on the Net::HTTP class (where Net is a module defining the Net namespace, and HTTP is a subclass) that gets and prints (hence get_print) the web page at Ruby Inside: The Ruby Blog.

 

It’s just as easy to put the contents of any web page into a string, for further manipulation by your program:

require 'net/http'
url = URI.parse('http://www.rubyinside.com/')
response = Net::HTTP.start(url.host, url.port) do |http| http.get(url.path)
end
content = response.body

In this example, you use the URI library (another standard library, and one that’s loaded automatically by net/http) to decipher a URL such as http://www.rubyinside.com/ into its constituent parts for the net/http library to use to make its request.

 

Once the URL has been parsed, an HTTP connection is “started,” and within the scope of that connection, a GET request is made with the get method (if this doesn’t make sense, don’t worry; it’s part of how the HTTP protocol works). Finally, you retrieve the content from response.body, a string containing the contents of the web page at http://www.rubyinside.com/.

 

OpenStruct

OpenStruct

Struct allowed you to create small data-handling classes on the fly, like so:

  • Person = Struct.new(:name, :age)
  • me = Person.new("Fred Bloggs", 25)
  • me.age += 1

Struct gives you the luxury of being able to create simple classes without having to define a class in the long-handed way.

 

The OpenStruct class provided by the ostruct library makes it even easier. It allows you to create data objects without specifying the attributes, and allows you to create attributes on the fly:

  • require 'ostruct'
  • person = OpenStruct.new
  • http://person.name = "Fred Bloggs"
  • person.age = 25

 

person is a variable pointing to an object of class OpenStruct, and OpenStruct allows you to call attributes whatever you like, on the fly. It’s similar to how a hash works, but using the object notation.

 

As the name implies, OpenStruct is more flexible than Struct, but this comes at the cost of harder-to-read code. There’s no way to determine exactly, at a glance, which attributes have been used. However, with traditional structs, you can see the attribute names at the same place the struct is created.

As you can see, using libraries is pretty easy. In most cases you just use require to load the relevant classes and methods, and then you can start using them right away. However, for more complex scenarios, read on!

 

RubyGems

RubyGems

RubyGems is a packaging system for Ruby programs and libraries. It enables developers to package their Ruby libraries in a form that’s easy for users to maintain and install. RubyGems makes it easy to manage different versions of the same libraries on your machine, and gives you the ability to install them with a single line at the command prompt.

 

Each individually packaged Ruby library (or application) is known simply as a gem or RubyGem. Gems have names, version numbers, and descriptions. You can manage your computer’s local installations of gems using the gem command, available from the command line.

 

RubyGems comes standard with Ruby nowadays, but it was not included with distributions of Ruby 1.8 (except in certain situations, such as with the default installation of Ruby on Mac OS X). You no longer need to be concerned with how it is installed as it’s available "out of the box"!

 

Finding Gems

It’s useful to get a list of the gems that are installed on your machine, as well as get a list of the gems available for download and installation. To do this, you use gem’s list command. If you run gem list from your command line, you’ll get a result similar to this:

*** LOCAL GEMS ***

bigdecimal (1.2.6)

json (1.8.1)

minitest (5.4.3)

It’s not much, but it’s a start. This list shows that you have three different gems installed, along with their version numbers. Your list of gems may be significantly longer than this, but as long as it looks like a list and not an error message, you’re good to go.

You can query the remote gem server (currently hosted by rubygems.org, but you can add other sources later) like so:

Within a minute or so, many thousands of gems and descriptions should go flying past.

 

Wading through such a list is impractical for most purposes, but generally you’ll be aware of which gem you want to install before you get to this stage. People on the Internet will recommend gems, or you’ll be asked to install a particular gem by this blog or another tutorial. However, if you wish to “browse,” the best way to do so is to visit rubygems.org, the home for the RubyGems repository. The site features search tools and more information about each gem in the repository.

 

Installing a Simple Gem

Installing a Simple Gem

Once you’ve found the name of a gem you wish to install, you can install it with a single command at the command line (where chronic would be replaced with the name of the gem you wish to install, although feedtools is a fine gem to test with):

 

gem install chronic

If all goes well, you’ll get output like this:

  • Fetching: chronic-0.10.2.gem (100%)
  • Successfully installed chronic-0.10.2
  • Parsing documentation for chronic-0.10.2
  • Installing ri documentation for chronic-0.10.2
  • Done installing documentation for chronic after 1 seconds 1 gem installed

 

First, RubyGems looks to see if the gem exists in the current directory (you can keep your own store of gems locally, if you like), and if not, it heads off to rubygems.org to download the gem and install it from afar. Last, it builds the documentation for the library using rdoc, and installation is complete. This process is the same for nearly all gems.

 

Note In many cases, installing one gem requires other gems to be installed too. That is, the gem you’re trying to install might have other gems it needs to operate, also known as "dependencies".

  • If you run gem list again at this point, your local list of gems will include the newly installed gem (in this case, chronic).
  • If you are aware that you need to install a specific version of a gem (such as version 0.10.2 of Chronic, as above), you can specify this like so:

gem install -v 0.10.2 chronic

 

Using Gems

As the RubyGems system isn’t an integrated part of Ruby, it’s necessary to tell your programs that you want to use and load gems. To demonstrate how gems can be used, you’ll install the redcloth gem, a library that can convert specially formatted text into HTML, ready to be used on a web page. Use gem install chronic (or sudo gem install chronic, depending on your setup), as demonstrated earlier, to install the gem.

Once the gem is installed, run irb or create a new Ruby source file, and use the chronic gem like so:

require 'chronic'

 

puts Chronic.parse('may 10th')

2016-05-10 12:00:00 +0100

In this example, we load the Chronic library with require and then we can use the Chronic class to do various things—in this case, time manipulation. Congratulations, you’ve used your first gem.

 

Upgrading and Uninstalling Gems

One of the main features of RubyGems is that gems can be updated easily. You can update all of your currently installed gems with a single line:

 

gem update

This makes RubyGems go to the remote gem repository, look for new versions of all the gems you currently have installed, and if there are new versions, install them. If you want to upgrade only a specific gem, suffix the preceding command line with the name of the gem in question.

Uninstalling gems is the simplest task of all. Use the uninstall command (where feedtools is replaced by the name of the gem you wish to uninstall):

 

gem uninstall feedtools

If there are multiple versions of the same gem on the machine, RubyGems will ask you which version you want to uninstall first (or you can tell it to uninstall all versions at once), as in this example:

$ gem uninstall rubyforge

 

Select RubyGem to uninstall:

1. rubyforge-0.3.0

2. rubyforge-0.3.1

3. All versions

 

Creating Your Own Gems

Naturally, it’s possible to create gems from your own libraries and applications.

Bundler

Bundler is a tool that was developed to help you manage the dependencies of a project (essentially, the libraries upon which your project depends) in a more structured way. It comes by default on some Ruby installs, but you can always ensure it's installed with gem install bundler.

 

Consider, for example, that you create a project that depends on several libraries or gems. To run this application locally, you'd need to make sure that you have the right versions of each gem installed on your system. But if you have numerous projects with wide varieties of dependencies, you'll start to find it hard to track which gems you have installed and what versions they are.

 

Bundler lets you create a file (called Gemfile) within a project's directory that specifies what libraries the project depends on. Here's an example of a very simple Gemfile:

source rubygems.org

gem 'nokogiri'

gem 'rack', '~>1.1'

 

This specifies where the gems are to be downloaded from by default, and then which two gems the current project depends upon. Nokogiri is specified without a version number, but in Rack's case, a version query is specified at the end of the line which says any version that's 1.1 or above (but not version 2 or above - so 1.2, 1.3, 1.4, etc.).

 

If you run bundle install from the directory where a Gemfile is present, Bundler ensures that the right gems are installed or upgraded to the right versions: Fetching gem metadata from your community gem host

Resolving dependencies...
Using mini_portile2 2.0.0
Installing rack 1.6.4
Using bundler 1.11.2
Using nokogiri 1.6.7.2
Bundle complete! 2 Gemfile dependencies, 4 gems now installed.

Use 'bundle show [gemname]` to see where a bundled gem is installed.

 

The correct gems, as specified in Gemfile, are now installed. You can use these from within your project, or if you want to ensure that the right versions are loaded, you can specify require 'bundler/setup' within your program, like so:

require 'bundler/setup'

require 'rack'

# Now Rack 1.1 or above is loaded properly

 

One other thing to be aware of is that when you install or upgrade gems, another file is created or updated called Gemfile.lock. This is not a file you are meant to change yourself, but it simply reflects what the precise set of dependencies are, along with their version numbers, so that if you distribute the project anywhere else, the very same set of libraries and versions will be installed properly. Here's an example of the Gemfile.lock produced by the install above:

GEM
remote: https://rubygems.org/
specs:
mini_portile2 (2.0.0)
nokogiri (1.6.7.2-x86-mingw32)
mini_portile2 (~> 2.0.0.rc2)
rack (1.6.4)
PLATFORMS
x86-mingw32
DEPENDENCIES
nokogiri
rack (~> 1.1)
BUNDLED WITH
1.11.2

Even though our main Gemfile specifies any version of Rack over 1.0 and under 2.0, we specifically have 1.6.4 installed as that's the latest matching version at the time of writing. If I transferred this project to you in the future, however, 1.7 may be the latest matching Rack, and this could break the code. The Gemfile.lock’s job, therefore, is to explicitly communicate which versions of which libraries are working with the project right now.

 

Bundler is a powerful but ever-changing tool that may even be a core part of Ruby if you are reading this blog a year or two after its publication. For this reason, I advise you visit its homepage at http://bundler.io/ to learn more about its operation and any new developments that may have occurred, as you will run into Bundler in many situations when working with other Ruby developers.