More Common Trip-ups for New Rubyists

Share this article

More Common Trip-ups for New Rubyists

Fotolia_50633248_Subscription_XXL-272x300

Self and Other Objects

In my previous article about trip-ups, we saw that class-level methods are defined like this:

class MyClass
  def self.hello
    "hello"
  end
end

MyClass.hello # => "hello"

Developers coming from other languages might be tempted to assume that def self.method_name is some kind of special syntax for creating class methods. However, this is also possible:

class MyClass
  def MyClass.hello
    "hello"
  end
end

MyClass.hello # => "hello"

And much more importantly, this is also possible:

MyClass = Class.new
def MyClass.hello
  "hello"
end

MyClass.hello # => "hello"

So, you can see that Ruby lets you define methods on classes. But classes are just objects of class Class, so logically you should be able to define methods on other kinds of objects, like a string.

a_string = "blerp"
def a_string.hello
  "hello"
end

a_string.hello # => "hello"

As it turns out, def self.whatever isn’t special syntax. Ruby allows you to define methods on objects, including classes, and on some kind of object named self.

But what is self?

For any line of Ruby code, there is a reference called self. self always refers to the current object at that point in the code, which is important because many other aspects of Ruby rely on it. For example, instance variables are just variables stored in self.

# At the top level, self is a special object called "main"
self # => main
self.class # => Object

# class definitions change the value of self
class SelfClass
  self # => SelfClass

  # Will be called on instances
  def self
    self
  end

  # Same as SelfClass.self
  def self.self
    self
  end
end

SelfClass.self # => SelfClass
SelfClass.new.self # => Instance of SelfClass

So who cares, right? Well, the fact that self is always something has some interesting consequences:

  • Instance variables can be assigned and resolved anywhere (although it might not make sense semantically, it’s always syntactically possible)
  • Methods can be defined anywhere

This is markedly different from other languages, where there are strict rules about where certain things can happen.

Lazy Initialization

Developers who are accustomed to languages that require instance variables to be declared in classes might be tempted to “define” instance variables in constructors like this:

class Item
  attr_accessor :title

  def initialize
    @title = ""
  end
end

There are some problems that come with doing things this way:

  1. Subclasses might need to worry about calling super.
  2. Objects get allocated regardless of whether they are used.
  3. It’s not possible to tell what a getter is going to return just by looking at it.

Part 1 showed that an unassigned instance variable returns nil instead of raising an error. This can be combined with the ||= operator to set default values without a constructor. It performs the assignment only if the left-hand operand is falsy, meaning nil or false.

class Item
  attr_writer :title

  def title
    @title ||= ""
  end
end

Now, it is obvious that when an Item instance receives the #title message, it will either return an empty string or the most recently assigned title. Even better, if we don’t care what an item’s title is, memory for it never gets allocated.

Scope Resolution Operator (::)

A problem can occur when there is a module or class in your code that has the same name as a core Ruby module that you want to use.

module MyModule
  class File
  end

  class Thing
    def exist?(path)
      File.exist?(path)
    end
  end
end

thing = MyModule::Thing.new
thing.exist?('/etc/hosts') # => udefined method `exist?' for MyModule::File:Class

The easiest way to fix this is to add the :: operator to the beginning of the colliding constant.

module MyModule
  class File
  end

  class Thing
    def exist?(path)
      ::File.exist?(path)
    end
  end

end

thing = MyModule::Thing.new
thing.exist?('/etc/hosts') # => true

Everything is Run Time

Historically, languages have made a distinction between what is declared and what is run. For example, in C++, classes are declared at compile-time, and instances are created and used at run-time. This can be frustrating, since compile-time code can be limited by a lack of information access.

In Ruby’s world, everything is run-time.

class Hello
  puts "Hello World"
end

This example prints the string because Ruby class “declarations” are just code that is being run like anywhere else. That opens a window into all kinds of possibilities – not the least of which is writing code that writes code. It isn’t necessary to have a special plugin container for your app when the code can change the app itself.

For example, sometimes it might be useful to define methods dynamically:

print "What noise does the animal make?: "
speak_noise = gets

Animal = Class.new do
  define_method :speak do
    puts "Animal: #{speak_noise}"
  end
end

Animal.new.speak

It’s also possible to inherit dynamically (mostly for impressing friends, but who knows?)

class RandomClass < [Array, Hash].sample
end

RandomClass.superclass # => either Array or Hash

Internal DSLs

Domain-Specific Languages enable developers to write elegant code that is tailored for particular situations. Example DSLs include RSpec, Cucumber, Prawn, and Shoes.

DSLs come in two flavors:

  1. internal – are based on a subset of the host language
  2. external – use a full-blown parser

Since Ruby is a flexible, syntax-noise free language, it is well-suited for developing internal DSLs. These are DSLs that are actually just Ruby, but tailored for a specific domain.

So, how does one go about creating one of these things?

In part 1, Ruby modules were shown to be packages for methods that could be mixed into classes. If you check a class’s #ancestors, you will see any modules that have been included.

Looking at the ancestors of Object, you will notice an odd module:

Object.ancestors #=> [Object, PP::ObjectMixin, Kernel, BasicObject]

Every object in Ruby ships with the Kernel module included. Kernel contains methods like #puts and #lambda. One way that DSLs are often created in Ruby is by adding methods to Kernel so that they will work at the top level.

module Kernel
  def task(name, &block)
    tasks << block
  end

  def tasks
    @tasks || @tasks = []
  end

  def run_task
    tasks.each { |t| t.call }
  end
end

Now, in a file using our DSL, we can run:

task :hello do
  puts "Hello world"
end

task :goodbye do
  puts "Later world"
end

run_tasks

Of course, run_tasks is superfluous and would probably be called underneath the hood in a real-world DSL.

instance_eval and class_eval

DSLs would be pretty limited if defining methods in Kernel was the only way to make them. Fortunately, Ruby provides #instance_eval. When provided a block or a string, the contents will be evaluated within the context of the instance.

a_string = "word"
a_string.instance_eval("reverse") # => "drow"

Let’s make a more portable version of our task running DSL. When using #instance_eval in this way, the methods defined form the keywords of the DSL. We’ll just make one DSL keyword: task.

module TaskList
  # Make every instance method a class method
  # Shorter than putting "self" in front of every method
  extend self

  def run(&block)
    instance_eval(&block)
    process_tasks
  end

  private

  def task(name, &block)
    # Leaving the "&" off converts it to a storeable proc
    tasks << block
  end

  def tasks
    @tasks || @tasks = []
  end

  def process_tasks
    tasks.each { |t| t.call }
  end
end

TaskList.run do
  task :hello do
    puts "Hello"
  end

  task :goodbye do
    puts "Goodbye"
  end
end

That version uses #instance_eval on a module. Don’t let that confuse you – modules are indeed instances of class Class. Here is the instance in a typical sense version (i.e. an instance of a class that’s not Class):

class TaskList
  def run(&block)
    instance_eval(&block)
    process_tasks
  end

  private

  def task(name, &block)
    tasks << block
  end

  def tasks
    @tasks || @tasks = []
  end

  def process_tasks
    tasks.each { |t| t.call }
  end
end

TaskList.new.run do
  task :hello do
    puts "Hello"
  end

  . . .
end

Unlike #instance_eval, #module_eval / #class_eval only works on modules and classes, respectively. An instance method defined in a #class_eval block will be defined for all objects of that class. This can be useful for monkey-patching core classes.

String.class_eval do
  def spaceify
    self.split("").join(" ")
  end
end

"hello".spaceify # => "h e l l o"

Privacy

Let’s say you have a class with some instance variables and a private method like this:

class Secret
  def initialize
    @hidden = 123
  end

  private

  def hidden_method
    "You can't call me!"
  end
end

Since instance variables need methods to be manipulated from outside the class, they are private, right? Well…

secret = Secret.new
secret.instance_eval { @hidden } # => 123
secret.instance_eval { hidden_method } # => "You can't call me!"

Ok, but even with #instance_eval, someone would need to know the names of instance variables and methods, right?

secret.instance_variables # => [:@hidden]
secret.private_methods # => [:initialize, :hidden_method]

It’s pretty easy to look inside a Ruby object and see what it owns and what it can do. This is known as reflection.

secret.hidden_method # => NoMethodError: private method `hidden_method'...

Calling private methods on an object won’t work. But what if we send a message to the object?

secret.send(:hidden_method) # => "This method is private - you can't call me!"

The way private methods actually work in Ruby is interesting in its simplicity. Private methods are just methods that cannot be called with an explicit receiver. This means that private methods can’t be called with an explicit receiver even if the explicit receiver is self.

class Secret
  def initialize
    self.private_method
  end

  private

  def private_method
    "You can't call me!"
  end
end

secret = Secret.new # => private method `private_method' called for...

As such, encapsulation in Ruby pretty much works on the honor system. There is no real privacy, only pseudo-privacy. Does that mean it’s acceptable to just go around breaking encapsulation? Well, no. If a developer made something private, it was probably for a good reason. Nevertheless, you will often see it, and it’s good to know why a developer might be sending a message to an object instead of just calling the method.

Murky Methods

Many programming languages rely on a strong relationship between what a class is and what it can do. In Ruby, this relationship breaks down. Rather than being concerned where an object comes from, it’s more important to think about what kind of messages it can receive.

In Ruby, if an object knows how to handle a message we send it, then it is probably the right kind of object. This way of thinking is commonly referred to as duck typing.

For example, this would not be good Ruby code:

if user.is_a? EmailUser
  "Email:" #{user.email}"
end

Although such type checks can improve the robustness of our code, they completely destroy the decoupling benefits of duck typing. Instead, it’s better to ask whether a class can respond to the messages you #send it.

if user.respond_to?(:email)
  "Email: #{user.email}"
end

So far, nothing too strange. However, this is where things take a turn for the awesome. Since objects receive messages in order to call methods, it’s possible to intervene when a message is received. This allows calls to methods that don’t even exist. Such an ability enables the development of flexible classes that can include new features without the modification of a single line of code (for example, the client of a web API).

To see how we can do this, let’s look at the steps Ruby takes when an object receives a message:

  1. Is the method defined in the receiver’s class or anywhere in its ancestors?
  2. Is #method_missing defined in the receiver’s class or anywhere in its ancestors?
  3. Raise NoMethodError

So, to handle undefined methods, just define #method_missing.

class MethodHandler
  def method_missing(method_name, *args)
    puts "Handling instance method #{method_name}"
    puts "The arguments were: #{args.join(', ')}"
  end
end

handler = MethodHandler.new
handler.iammadeup!(1, 2, "cat")

A common pattern is to check the method name against a regular expression, and otherwise Raise NoMethodError by calling super (unless the parent class has defined method_missing). Note that the method name is a symbol, not a string. This is important to keep in mind when creating regular expressions, since symbols start with a colon. Some developers prefer to call it method_sym to be more descriptive.

Unfortunately, Even if #method_missing responds to a method, it does not mean that #respond_to? or #method will. Therefore, it’s important to always define #respond_to_missing when defining #method_missing.

Thoughtbot has a good post on the subject here.

Singleton Classes / Eigenclasses

It is difficult to explain what comes next. I think it is best introduced with a couple of statements:

  1. Methods are really defined on classes, not objects.
  2. Any Ruby object can have its own methods.

Sound contradictory? Let’s prove the second statement with some code.

word = "Hello"
def word.double
  self * 2
end
puts word.double # => "HelloHello"

Both of these statements are true for Ruby. And yet – how? If methods live inside classes, how could an object have methods that are not defined for its class or its ancestors? There must be…another class. Somewhere, there must be a hidden class in which such methods live.

puts word.method(:length).owner # => "String"
puts word.method(:double).owner # => "#<Class:#<String:0x00000001bf4680>>"

And this is, indeed, the case.

It’s best to not get attached to any particular name for this hidden class, as several have been used over the years: metaclass, eigenclass, ghost class, singleton class, anonymous class. Officially, Ruby has adopted #singleton_class in 1.9.2. Note that the word “singleton” is not being used in the singleton pattern sense. Since this can be confusing, the term “eigenclass” will be used in this article from here on out.

The canonical way to define things in an object’s eigenclass is to use

class << [some object]

notation.

letters = ['a', 'b', 'c']

class << letters
  def capitalize
    self.map do |letter|
      letter.upcase
    end
  end
end

letters.capitalize #=> ['A', 'B', 'C']

Earlier we saw that private class methods are not so private. This can be fixed by defining the methods on the eigenclass.

class Secretive
  class << self
    private

    def hidden
      "This method is private - You can't call me!"
    end
  end
end

Secretive.hidden #=> private method `hidden' called for Secretive:Class (NoMethodError)

Making attr_accessor-like Methods

In part 1, we saw how Ruby lets you define getters and setters with the helper methods attr_reader, attr_writer, and attr_accessor.

class Foo
  attr_accessor :bar, :baz
end

What if we want to make your own helper methods like this? It makes sense to define a class-level method since self is the class inside the class definition.

class Foo
  def self.attr_printer(*properties)
    puts "attr_printer called with #{properties}"
  end

  attr_printer :bar, :baz
end

This works, but defining the method inside each class that uses attr_printer kind of defeats the purpose. We need to find another place to define it that will apply to all classes.

class Class
  def attr_printer(*properties)
    puts "attr_printer called with #{properties}"
  end
end

class Something
  attr_printer :attr_one, :attr_two, :attr_three
end

Now all classes have the new method, but we might not want that, either. Instead, it’s probably better to put the code into a module that can be included into classes individually, especially if there are varying implementations of the helper method. Here is a chance for us to leverage eigenclasses again.

module AttrPrinterable
  def self.included(klass)
    class << klass
      def attr_printer(*properties)
        puts "attr_printer called with #{properties}"
      end
    end
  end
end

class Something
  include AttrPrinterable
  attr_printer :attr_one, :attr_two, :attr_three
end

Alternatively, you can use #instance_eval on the Class instance. Note that def attr_printer and not def self.attr_printer is used.

module AttrPrinterable
  def self.included(klass)
    klass.instance_eval do
      def attr_printer(*properties)
        puts "attr_printer called with #{properties}"
      end
    end
  end
end

Summary

  • self is always the current object.
  • Rather than declaring instance variables in constructors as in static languages, it’s sometimes better to take advantage of lazy initialization methods.
  • #instance_eval runs a block or string in the context of an instance, including instances of Class
  • Use #class_eval to define methods for all instances of a class.
  • There is no difference between compile-time and run-time code in Ruby.
  • In Ruby, a constant is any word that begins with a capital letter.
  • Constants are mostly used for class and module names, never as enums like in other languages.
  • The scope-resolution operator :: is used to specify constants
  • Put :: at the front of a constant to specify a root level constant.
  • Private methods are methods that cannot have an explicit receiver, including self.
  • Objects receive messages to call methods.
  • The . operator is shorthand for #send to send a message to an object
  • #method_missing can intervene when an object does not know how to respond to a message.
  • It’s important to define #respond_to_missing when defining #method_missing.
  • An eigenclass or singleton class is a unique class an object is linked to.
  • The class << syntax can be used to open an object’s eigenclass, including a class object.
  • Class methods cannot be private, but eigenclass methods defined on classes can.
  • Methods like attr_accessor can be defined as class-level methods of classes or instance-level methods of Class.
Robert QuallsRobert Qualls
View Author

Robert is a voracious reader, Ruby aficionado, and other big words. He is currently looking for interesting projects to work on and can be found at his website.

GlennG
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week
Loading form