The Well-Grounded Nuby
Advice for Ruby newcomers (and their mentors)
David A. Black (@david_a_black)
(These are the notes from the September 2010 Philly.rb meeting)
We want to learn the major points of confusion in the Ruby language and how to navigate them.
There are 7 big points that hurt. Understanding these 7 points can help you along the way while working through your Ruby education:
- Point #1: Every expression evaluates to an object
- Not everything is an object (i.e. if statements, argument lists, keywords, etc.), but there is just enough non-objects to make ALMOST everything an object…
- BUT everything, every statement, will evaluate to an object. The result of any code block will be an object. Even nil is an object.
- Use irb. It is awesome.
- puts ALWAYS returns nil, it does NOT return its argument: It prints the argument.
- a failed if statement returns nil
-
# in Ruby 1.8: p 'string' # evaluates to nil
# but in Ruby 1.9 it evaluates to the argument: p 'string' # evaluates to 'string' - Class definitions return nil.
- Point #2: It is ALL about message passing
- all the infix syntax is actually shorthand for message passing:
- so…
- 3+2 is actually 3.+(2)
- 5*3 is actually 5.*(3)
- a[2] is actually a.[](2)
- +x is actually x.+@
- What is cool about this, is that you can roll your own infix operators by just defining them as message.
-
case some_string when "abc" #... when /def/ #.... end
# is the same as...
if "abc" === some_string #... elseif /def/ === some_string #... end
# which is actually...
if "abc".===(some_string) #... elsif /def/.===(some_string) #... end - That means if you override the === method, you can create your own case statements!!! (That is pretty awesome.) Like so:
class Person attr_reader :name
def initialize(name) @name = name end
def ===(other_person) self.name == other_person.name end end
trev = Person.new('trevor') trevor = Person.new('trevor') bob = Person.new('bob')
case trev when bob # not yet... when trevor # this will work, since we overrode the === method! end - The === method is mostly the same as the ==, but it is meant to be overwritten for this use.
-
String === String # is false, but... String === "abc" # is true - So, don’t do: case “abc”.class
- Point #3: Objects resolve messages into methods
- Objects do not “have” methods
- They have the intelligence to find methods when they are required to do so.
- Methods are stored in classes and modules, not in the objects themselves.
- Every object has a lookup-path to find methods in that objects class hierarchy:
- The singleton class: this is where you define a method for an individual object. (Woah, this is cool)
str = "abc" def str.make_big str.upcase end
str.make_big # this works
str2 = "def" str2.make_big # this does not work, since make_big is defined for the str object only - Then it looks for any modules in the object’s singleton class
- Then it looks at the object’s class
- Then it will look at the object’s class’ included modules
- Then it will move on up the tree (class, module, class, module) until you reach the Object class (or BasicObject in Ruby 1.9)
- The singleton class: this is where you define a method for an individual object. (Woah, this is cool)
- Lookup-paths are dynamic. Objects created before a module is added to the base class will have access to the added methods.
class Person end
trev = Person.new
class Person def talk "rubyrubyruby" end end
trev.talk # is "rubyrubyruby", since the lookup happens at RUNTIME, not object creation time. - There is an extend method that will add a module’s methods to one object, using the singleton object.
- Point #4: Classes and Modules are objects
- “The answer to 75% of all questions about Ruby is: classes are objects, too.”
- You can send messages to them.
- They can be put in arrays.
- They can be assigned to local variables
- They have their own instance variables, just like other objects. These are instance variables for the CLASS, not the objects instanced by the class.
- Class variables are hierarchy-scoped globals. Don’t use them. They are evil, like eval evil. They are mini- globals!
- Of course, classes are special beings, but they and still objects.
- So, treat you classes as normal objects!
- Point #5: There is always a self
- self is the default receiver of messages
- self owns instance variables, and, by definition, any instance variables you see belong to self.
- The value of self changes:
- in classes, it is the class
- in methods, it is the object running the method
- outside of objects, it is main (this is just a holder for objects)
class C self # I am the class C
def m self # I am an instance of the class C end end
self # I am main - instance_eval swaps out self for the duration of the code block.
- class_eval is a special flavor of instance_eval
- Point #6: Variables contain references to objects
- In Ruby, every variable contains a reference to an object. And every object can have access to any number of variables.
- This means that every variable is passed by reference, not as a copy of the object.
str = "abc" str2 = str str2 << "def" str # is "abcdef" since both str and str2 point to the same object - Ruby exposes references to instance variables in objects, so you can abuse this, unless you write some extra code
class Person attr_reader :name # this should not be writable
def initialize(name) @name = name end end
trev = Person.new('trev') trev.name # this is 'trev' trev.name << " j00 have been hacked!" trev.name # this is 'trev j00 have been hacked!'
# To stop this, don't use attr_reader: class Person def initialize(name) @name = name end
def name @name.dup end end
trev = Person.new('trev') trev.name # this is 'trev' trev.name << " j00 have been hacked!" trev.name # this is 'trev', since it is just a dup of the instance variable @name
- Point #7: true and false are objects AND true and false are states
- If a test evaluated to the object nil or the object false: it is false, otherwise it is true.
Thanks to David A. Black, and the entire Philly.rb crew for a great talk!

