The Key Ideas of the Ruby Programming Language

Part of what makes the Ruby programming language so effective is that it takes a small number of concepts and applies them throughout the whole design of the system. Once you know these fundamental principles it is both simple to learn, and incredibly flexible. This article is my attempt to define the essential ideas of Ruby without getting into the specifics of language syntax.

  1. Object Orientated at All Levels
  2. Code in Easily Reusable Units
    1. Ruby Gems
  3. The Structures of Your Program are Dynamic
  4. No Arbitrary Access Restrictions
  5. Variables are Just Pointers
  6. Blocks Neatly Enclose Actions
  7. Hashes as Universal Data Containers
  8. Methods Are Always Functions
  9. Symbols for Efficiency
  10. Everything is True, Unless You Say Otherwise
  11. Brackets Are Just Reading Aids

Object Orientated at All Levels

Ruby itself is completely object-orientated, even if you write procedural-style code with it. This means that every action is carried out by passing a message to an object, to run a method, even if a different syntax is used in any context. Under the covers, variable assignments call constructor methods, and data type operators are just instance methods. Ruby refers to the properties of objects as attributes.

The only things in Ruby that are not full objects are variable names, modules, and blocks (see below).

Code in Easily Reusable Units

Ruby provides the concept of a module, a container for grouping one or more pieces of code. Modules may hold class definitions, as well as methods and variables that are not part of a specific class. Importantly, they can vary in scale from small snippets to complex software libraries.

You may not only call specific items of code from a module, but also use an include statement at any point to in your code mix-in all of the code from a named module at that position, as if it had been written at the line where the include statement appears. Code within a module may also call or mix-in from any other modules that are available.

Combined with class inheritance, modules enable you to organize and reuse code very efficiently both within and between software. Ruby only permits a class to inherit an initial set of methods and attributes from one parent class, but you may freely use module mix-ins within a class definition to supplement the properties and methods that the child class inherits from the parent. When you write your own classes you commonly mix-in the content of built-in Ruby modules such as Enumerable to provide your new class with standard methods.

The load path global variable defines the complete set of directories that the runtime should check for source files. This variable is automatically generated at run-time, and you may modify it like any other variable.

Ruby Gems

The RubyGems component of the platform massively extends the code reuse facilities offered by the Ruby language itself. Every library, application, or other collection of files that are configured as a gem may be deployed and upgraded across all systems that have a version of Ruby installed. The public servers at RubyForge provide a global distribution point for Open Source Ruby applications, and you may also offer your own RubyGems server for public or private use, using the supplied tools.

The Structures of Your Program are Dynamic

Your Ruby code can add, remove, or modify the methods and attributes that are attached to any object at any time. In addition to working with individual elements of an object, you can also extend it, injecting the complete contents of modules into the active state of that object.

Class definitions are themselves standard objects (instances of the built-in class Class), and can be created or modified in the same way that you can generate and redefine objects that are instances of a class.

This freedom means that you can write code that not only modifies the behavior of individual objects, but also changes the structure of itself as it runs, reshaping the class definitions to adapt to requests or conditions exactly as required. Equally, you can easily write software that generates whole new programs on demand.

You may not overload a Ruby method to have multiple definitions simultaneously, as you can with some other languages. Instead, write a definition that works with a flexible set of arguments. The built-in support for variable-length argument lists makes this possible.

No Arbitrary Access Restrictions

By default, there are no special protected types or classes to limit changes or reuse. You may creates classes that inherit from any class from any available module, and modify classes provided by the platform or third-party libraries in exactly the same way that you modify your own. There are facilities to make objects read-only when you absolutely need to do so.

Variables are Just Pointers

All variables in Ruby are references, just named pointers to objects, and have no actual data type themselves. When you assign a value to a new variable that value becomes a new object, and the variable is the initial pointer that you use to refer to it. You either explicitly define the class of of the object that you create, or let Ruby use built-in rules to automatically determine the class to use for the new object. This means that Ruby is effectively a strongly typed language, even though you do not assign types to variables.

Once created, a variable may be reassigned to point to a different object at any time, and will then respond to calls using the methods of the new object. Modules enable you to easily add standard sets of methods to your classes, so that duck typing occurs, where your code simply specifies a generic method on a variable, and the underlying object that the variable currently represents will respond in an appropriate way.

Blocks Neatly Enclose Actions

Unlike object orientation and modules, blocks (also known as closures) are not yet a mainstream programming concept. In Ruby, blocks are the standard means of executing code on sequences of values. A block simply defines (encloses) an in-line piece of code that may accept some parameters from the method call next to it.

Every method should accept a block as an optional argument, and return values to such a block. The block then runs the method calls or other code that it encloses, using the provided values as variables. If a method will return a series of values then these are passed to the block one at a time for processing.

Although a block is not an object, you may define Proc objects, which each hold a block along with any required local variables. Any method that accepts a block as an argument will also accept a Proc object.

Hashes as Universal Data Containers

Ruby uses the term hashes for associative arrays, or dictionaries as they are known in Python. A hash holds one or more pairs of keys and values. Both the key and the value variable in each key-value pair may point to objects of any class, including other hashes or arrays.

The Ruby platform includes methods to directly store and retrieve instances of Ruby objects such as hashes from files, or from Ruby programs that are running on other networked machines, which means that potentially any data can be held in a hash and manipulated with standard methods.

Methods Are Always Functions

By default, every method in Ruby returns the last value that was assigned, which your code may capture if you wish. This means that you do not need to explicitly set up methods as functions, and can effectively obtain a return code from every method. You may override this behavior, and explicitly specify the value that a method returns.

Where a method should be silent, simply have it return nil (the Ruby type for null values).

Symbols for Efficiency

A symbol is an object, derived from the Symbol class, used instead of a string for method arguments, attributes, and hash keys. Each symbol has one record in a global symbol table, so that a symbol remains unique even when it is used in several different contexts. Ruby itself also tracks class, method, and variable names as symbols.

As well as being limited to a single instance, each symbol consumes far less memory than an equivalent string. The total memory savings become significant when a Ruby application must handle very large numbers of objects.

Everything is True, Unless You Say Otherwise

In Ruby, the keyword nil denotes a null value. The values nil and false are false for boolean tests. All other values return true.

Brackets Are Just Reading Aids

Brackets (parentheses) in method calls are purely for readability in Ruby, and you may include or omit as you wish. Use them to denote where a list of parameters begins and ends, or leave them out for concise code snippets and domain-specific languages (DSLs).

Legal Note: The License

All original content is © 2010, Stuart Ellis.

This material is provided under the Creative Commons Attribution-Share Alike 3.0 License.