KEMBAR78
Ruby objects | KEY
Objects in Ruby
     Reuven M. Lerner
    reuven@lerner.co.il
   September 21st, 2010
Warning!
Warning!

• We’ll be getting into seriously hairy object
  stuff today.
• This is both fun and useful, but can be
  tricky.
• Please stop me and ask questions whenever
  you need!
Dynamic typing
• Ruby uses dynamic typing — meaning that
  variables don’t have types
  >> a = [1,2,3]
    => [1, 2, 3]
  >> a = 'hello'
    => "hello"
  >> a = {:a => 5, :b => 6}
    => {:b=>6, :a=>5}
Strict typing
• But data does have types, and those types are
  strictly enforced:
  >> puts 5 + "5"
  TypeError: String can't be coerced
  into Fixnum
          from (irb):30:in `+'
          from (irb):30
          from :0
Everything is an object!
• Numbers and Strings
• Arrays and hashes
• true, false, nil
• Regular expressions, ranges
• Files
• Classes, Modules
Object ID?
• Every object in Ruby has an object ID, a
  unique integer
• Builtin objects have permanent IDs
• Objects that you define or create have
  their own object IDs
• If the object IDs are equal, then we’re
  talking about the same object
Object IDs
>> a = [1,2,3]
  => [1, 2, 3]
>> a.object_id
  => 2164172220
>> b = [1,2,3]
  => [1, 2, 3]
>> b.object_id
  => 2164142300
>> a = b
  => [1, 2, 3]
>> a.object_id - b.object_id
  => 0
What is equality?

• You can compare them with ==, to see if
  their contents are equal
• You can compare their object IDs, to see if
  they point to the same piece of data
That’s nice —
but what is an object?

• Every object is two things:
 • Some state (instance variables)
 • A pointer to a class
OK, and what is a class?
• A class is four things:
 • Some state (because they’re objects)
 • A pointer to a class (because they’re
    objects)
 • A parent class
 • Zero or more methods
Whoa!
• A class is an object?
 • Yes.
• What is its class?
 • Class. (Which is a constant identifier.)
• So a class is an instance of Class?
 • Yes.
Examples
>> 60.class

    => Fixnum

>> 60.class.class

    => Class

>> "hello".class

    => String

>> "hello".class.class

    => Class
Let’s define a class
>> class MyClass

>> end

>> o = MyClass.new

    => #<MyClass:0x102ffdef0>

>> o.class

    => MyClass
Let’s define a class
>> class MyClass
                     Class definition
>> end

>> o = MyClass.new

    => #<MyClass:0x102ffdef0>

>> o.class

    => MyClass
Let’s define a class
>> class MyClass
                     Class definition
>> end

>> o = MyClass.new   New object, class is MyClass
    => #<MyClass:0x102ffdef0>

>> o.class

    => MyClass
Let’s define a class
>> class MyClass
                     Class definition
>> end

>> o = MyClass.new   New object, class is MyClass
    => #<MyClass:0x102ffdef0>

>> o.class
                     See? I told you!
    => MyClass
Methods
• Methods are defined on classes
• When we invoke a method on object o,
  Ruby looks in o’s class (MyClass) for a
  method of that name
• Ruby does this at runtime
• Ruby has only one type of method!
 • No static, virtual, final, abstract, etc.
Method example
>> class MyClass
>>   def say_hello
>>      puts "hello"
>>      end
>>   end
  => nil
>> o = MyClass.new
  => #<MyClass:0x1036160d0>
>> o.say_hello
hello
  => nil
Method example
>> class MyClass
>>   def say_hello
>>      puts "hello"   Method definition
>>      end
>>   end
  => nil
>> o = MyClass.new
  => #<MyClass:0x1036160d0>
>> o.say_hello
hello
  => nil
Method example
>> class MyClass
>>   def say_hello
>>      puts "hello"   Method definition
>>      end
>>   end
  => nil
>> o = MyClass.new     New instance
  => #<MyClass:0x1036160d0>
>> o.say_hello
hello
  => nil
Method example
>> class MyClass
>>   def say_hello
>>      puts "hello"   Method definition
>>      end
>>   end
  => nil
>> o = MyClass.new     New instance
  => #<MyClass:0x1036160d0>
>> o.say_hello
hello                  Invocation of method
  => nil
No such method?
• What if we invoke a method that doesn’t
  exist?
  >> o.say_goodbye
  NoMethodError: undefined method
  `say_goodbye' for #<MyClass:
  0x1036160d0>
            from (irb):17
method_missing
>> class MyClass
>>    def method_missing(name)
>>      puts "You tried to execute the '#{name}'
method. Too bad!"
>>      end
>>    end
   => nil

>> o.say_goodbye
You tried to execute the 'say_goodbye' method.     Too
bad!
  => nil
method_missing
>> class MyClass    Symbol with method name
>>    def method_missing(name)
>>      puts "You tried to execute the '#{name}'
method. Too bad!"
>>      end
>>    end
   => nil

>> o.say_goodbye
You tried to execute the 'say_goodbye' method.     Too
bad!
  => nil
Method search order
• Ruby looks for a matching method in the
  object’s class
• If none is found, it tries in each ancestor
  class, in order
  • The class method “ancestors” returns an
    array of classes (and modules)
• If no match found, invokes method_missing
self

• The “self” keyword is built into Ruby
• It refers to the current object — the
  default receiver of messages
• Knowing the value of “self” during each
  point of execution is important
Inheritance!

• Every object has a class
• Every class (other than Object) has a
  superclass
• We’ll look for methods in every parent
  class until we find a match or die trying
Inheritance

• When a class is defined, we can set its
  superclass
  class MyClass # inherits from Object
  class MyClass < YourClass

• Single inheritance only!
Superclass
• How can we find the superclass? Ask the
  class!
  >> o.class
    => MyClass
  >> o.class.superclass
    => Object
  >> o.class.superclass.superclass
    => nil
Methods are messages

• When you say
  o.say_hello
• You’re really invoking the “send” method:
  o.send(:say_hello)
Mini-message sender

>> def send_a_message
>>   s = ('a'..'z').to_a.join
>>   print "Enter a method name: "
>>   puts s.send(gets.chomp.to_sym)
>> end
Method protection


• Methods are public by default
• You may declare a method “public,”
  “private,” or “protected”
public
• Anyone can invoke the method
  o.a_method
  o.send(:a_method) # same thing


  o.a_method(an_argument)
  o.send(:a_method, an_argument)
private
• Only self can invoke private methods
 • In other words, you cannot invoke
    self.method_name or o.method_name
  • And certainly not o.send(:method_name)
• Only instances of the class in which it was
  defined (and its subclasses) may use it
protected

• Not commonly used, in part because it’s
  hard to understand
• Only if the sender and receiver of the
  message inherit the method from a
  common ancestor, may it be invoked
Listing methods

• Get an array of method names (as strings)
 o.methods
 o.public_methods      # same as o.methods
 o.protected_methods
 o.private_methods
What about data?

• We said earlier that an object is data and a
  class pointer, and that methods sit on the
  class
• So, where’s the data?
Instance variables
• Variables that are defined on an object, or
  “instance variables,” have a @ prefix
• Often defined in the constructor
• If accessed without being set, equal to nil
• Unavailable outside of the object itself!
 • You must use methods to set or retrieve
    instance variables
Instance variables
>> o.instance_variables
 => ["@bar", "@foo"]
>> o.instance_variable_get('@bar')
 => 2
>> o.instance_variable_set('@bar', 10)
 => 10
>> o.instance_variable_get('@bar')
 => 10
Cool irb trick
>> irb o
>> instance_variables
  => ["@bar", "@foo"]
>> @blah = 100
  => 100
>> instance_variables
  => ["@blah", "@bar", "@foo"]
Using instance variables
 class Person
   def initialize(first_name='', last_name='')
       @first_name = first_name
       @last_name = last_name
   end
   def name
       @first_name + ' ' + @last_name
   end
 end
Defining getters
• Define VARNAME as a method:
  def first_name

    @first_name

  end



  puts o.first_name
Defining setters
• Define VARNAME= as a method:
  def first_name=(value)

    @first_name = value

  end



  o.first_name = 5
Attributes
• Instead of defining setters and getters for all
  of your instance variables, create “attributes”


  attr_reader :foo          # read-only
  attr_writer :bar          # write-only
  attr_accessor :baz        # read-write
Attribute example
class Person

  attr_accessor :first_name

  attr_accessor :last_name

  def initialize(first_name='', last_name='')

      @first_name = first_name

      @last_name = last_name

  end

end
Attribute example
class Person

  attr_accessor :first_name

  attr_accessor :last_name

  def initialize(first_name='', last_name='')

      @first_name = first_name
                                 Still need to initialize
      @last_name = last_name          our variables
  end

end
The story so far
• Data is private, stored in @variables
• Access to data is handled via methods
• Methods are on an object’s class, and can
  be kept private (but are public by default)
• The first matching method in the class’s
  ancestry is invoked
• No match?   method_missing or exception
Wait a second...

• When I use ActiveRecord, I say
  Person.find(:all)
• What does that mean?   How does that fit
  into what we already know?
Person.find
• Person.find is a method
• Methods are defined on the class of the
  object
• Person is an instance of Class
• So Person’s methods are defined in Class?
 • No, but that’s not a bad guess!
Indeed, we can do that
>> class Person
>>    end
   => nil

>> Person.blah
NoMethodError: undefined method `blah' for
Person:Class
        from (irb):33
        from :0
Class methods
>> class Class
>>   def blah
>>      puts "blah!"
>>      end
>>   end
  => nil
>> Person.blah
blah!
  => nil
>> Fixnum.blah
blah!
  => nil
Class methods
>> class Class
                       Open up the Class class
>>   def blah
>>      puts "blah!"    and define a method
>>      end
>>   end
  => nil
>> Person.blah
blah!
  => nil
>> Fixnum.blah
blah!
  => nil
Class methods
>> class Class
                       Open up the Class class
>>   def blah
>>      puts "blah!"    and define a method
>>      end
>>   end
  => nil
>> Person.blah            Yes, we can define
blah!                  class methods this way
  => nil
>> Fixnum.blah
blah!
  => nil
Class methods
>> class Class
                       Open up the Class class
>>   def blah
>>      puts "blah!"    and define a method
>>      end
>>   end
  => nil
>> Person.blah            Yes, we can define
blah!                  class methods this way
  => nil
>> Fixnum.blah
blah!
                           Er, if we want them
  => nil               to be defined for all classes
Hmm.
• Class methods exist.
• Methods are defined on an object’s class.
• We know that Person.find isn’t defined on
  Class, because it is specific to ActiveRecord.
• So where are class methods for Person
  defined?
Singleton methods

• Not the “singleton” design pattern!
 • A very unfortunate term
• Define a method on an object, not a class
• Occasionally useful for specific needs
• Important as a tool for understanding
Singleton example
>> a = "hello"
>> def a.say_something(something)
>>   puts something
>> end
>> a.say_something("hello")
  => hello
>> "another_string".say_something("hello")
  NoMethodError: undefined method `say_something'
  for "another_string":String
         from (irb):47
         from :0
Singleton example
>> a = "hello"
>> def a.say_something(something)
>>   puts something
>> end
>> a.say_something("hello")
  => hello
>> "another_string".say_something("hello")
  NoMethodError: undefined method `say_something'
  for "another_string":String
         from (irb):47
         from :0
Singleton example
>> a = "hello"
>> def a.say_something(something)
>>   puts something
>> end                          Notice the method is
>> a.say_something("hello")        defined on a!
  => hello
>> "another_string".say_something("hello")
  NoMethodError: undefined method `say_something'
  for "another_string":String
         from (irb):47
         from :0
Singleton methods

• We add a method to a particular instance
• But wait — methods only exist on classes
• What the heck is going on here?
Eigenclasses
Object



String



             Class is String


  a
Eigenclasses
Object



String      Class is String




  a
Eigenclasses
 Object



  String      Class is String


Eigenclass



    a
Eigenclasses
 Object



  String       Class is String


Eigenclass   New class in the middle


    a
Eigenclasses
 Object



  String       Class is String


Eigenclass   New class in the middle


    a
             Singleton method is defined here
Eigenclasses?!?
• Also known as “ghost classes” — each
    object has its own eigenclass for private
    method storage
•   When you define a singleton method on an
    object, you’re creating an eigenclass that
    sits between the object and its class
• Singleton methods are defined in the
    eigenclass
Singleton override
>> a = 'abc'       >> a.reverse
  => "abc"           => "ABC"
>> b = 'def'       >> b.reverse
  => "def"           => "fed"
>> def a.reverse   >> b = a
>>    upcase         => "abc"
>>    end          >> b.reverse
  => nil             => "ABC"
Defining class methods

• We define a singleton method by naming
  both the object and the method name:
  >> a = "hello"

  >> def a.say_something(something)
  >>   puts something
  >> end
Defining class methods
• So we can define a class method by naming
  both the object and the method name:
  >> def Person.say_something(something)
  >>   puts something
  >> end
    => nil


  >> Person.say_something('hello')
  hello
    => nil
Instance vs. class
          methods
• Rule: Methods for an object reside in its
  class
• Methods for p (an instance of Person) are
  defined in Person
• Methods for Person — class methods —
  are defined in Person’s eigenclass
• Eigenclass of a class == “metaclass”
Define class methods, 1
 >> class Person
 >>   def Person.foo
 >>    "foo"
 >>    end
 >>   end
   => nil
 >> Person.foo
   => "foo"
Define class methods, 1
 >> class Person
 >>   def Person.foo   Defining on Person
 >>    "foo"
 >>    end
 >>   end
   => nil
 >> Person.foo
   => "foo"
Define class methods, 2
 >> class Person
 >>   def self.bar
 >>    "bar"
 >>    end
 >>   end
   => nil
 >> Person.bar
   => "bar"
Define class methods, 2
 >> class Person
 >>   def self.bar     “self” in this
 >>    "bar"
                     context is Person
 >>    end
 >>   end
   => nil
 >> Person.bar
   => "bar"
Define class methods, 3
 >> class Person
 >>   class << self
 >>     def baz
 >>         "baz"
 >>         end
 >>     end
 >>   end
   => nil
 >> Person.baz
   => "baz"
Define class methods, 3
 >> class Person
 >>   class << self   Add to Person’s
 >>     def baz         eigenclass!
 >>         "baz"
 >>         end
 >>     end
 >>   end
   => nil
 >> Person.baz
   => "baz"
Why do we care?

• Why should I care about eigenclasses?
• How does this affect me?
• Couldn’t you think of a sillier name?
It’s really important
• Once you understand eigenclasses, you
  basically understand the entire Ruby object
  model
• There are no exceptions to this rule of
  how things work
• Suddenly, instance methods, class methods,
  and modules fall into place
Oh, and by the way
• We still haven’t explained how we defined
  Person.find
 • We know how to define a class method
    on a single class
 • But how do we define a class method for
    anything inheriting from
    ActiveRecord::Base?
Answer
• Define it on the eigenclass of the base class!
  >> class A
  >>   class << self
  >>     def boo
  >>         "boo"
  >>         end
  >>     end
  >>   end
    => nil
That’s right:
• OK, I admit it: Ruby is a bit sneaky here.
• The superclass of the eigenclass of an
  object is the object’s class.
• But the superclass of a class’s eigenclass is
  the eigenclass of the class’s superclass.
• Or if you prefer: The superclass of the
  metaclass is the metaclass of the superclass.
More on Eigenclasses
      Class


   Eigenclass of
ActiveRecord::Base


                     Class is Class


     Person
More on Eigenclasses
      Class          Class is Class

   Eigenclass of
ActiveRecord::Base




     Person
More on Eigenclasses
      Class          Class is Class

   Eigenclass of
ActiveRecord::Base


    Eigenclass



     Person
More on Eigenclasses
      Class                   Class is Class

   Eigenclass of
ActiveRecord::Base


    Eigenclass       New class in the middle


     Person
More on Eigenclasses
      Class                   Class is Class

   Eigenclass of
ActiveRecord::Base


    Eigenclass       New class in the middle


     Person
                            “find” is defined here
More on Eigenclasses
      Class                   Class is Class

   Eigenclass of
ActiveRecord::Base


    Eigenclass       New class in the middle


     Person
                            “find” is defined here
Unhiding eigenclasses
class Object
  def eigenclass
      class << self
        self
      end
  end
end
Congratulations!


• You’ve made it through the hard part
• Now we can move onto modules!
Modules

• Modules have two uses:
 • Namespaces
 • Keep related code together, for inclusion
    as a whole later on
Modules vs. Classes
• Both are capitalized (i.e., they’re constants)
• Both can contain methods
• Classes are modules (but modules are not
  classes)
• They may contain constants
• “self” inside refers to the module/class
Modules vs. classes

• Regular (instance) methods defined in a
  module cannot be accessed directly
• Module methods defined in a module may
  be accessed just like class methods
 • They’re also defined just like class
    methods
Module methods, 1
>> module Blah
>>   def Blah.foo
>>    "foo"
>>    end
>>   end
  => nil
>> Blah.foo
  => "foo"
Module methods, 1
>> module Blah
>>   def Blah.foo   Defining on Blah
>>    "foo"
>>    end
>>   end
  => nil
>> Blah.foo
  => "foo"
Module methods, 2
>> module Blah
>>   def self.foo
>>    "foo"
>>    end
>>   end
  => nil
>> Blah.foo
  => "foo"
Module methods, 2
>> module Blah
>>   def self.foo   self here is Blah
>>    "foo"
>>    end
>>   end
  => nil
>> Blah.foo
  => "foo"
Module methods, 3
>> module Blah
>>   class << self
>>     def baz
>>         "baz"
>>         end
>>     end
>>   end
  => nil
>> Blah.baz
  => "baz"
Module methods, 3
>> module Blah
>>   class << self   Add to Blah’s
>>     def baz        eigenclass!
>>         "baz"
>>         end
>>     end
>>   end
  => nil
>> Blah.baz
  => "baz"
Constants in modules
>> Math::E
 => 2.71828182845905
>> Math::PI
 => 3.14159265358979
>> Math.PI
NoMethodError: undefined method
`PI' for Math:Module
       from (irb):99
Constants in modules
>> Math::E
 => 2.71828182845905
>> Math::PI
 => 3.14159265358979
>> Math.PI    Methods aren’t constants!
NoMethodError: undefined method
`PI' for Math:Module
       from (irb):99
Nested modules
>> module Stuff
>>   module Things
>>       def self.hello
>>         "hello"
>>         end
>>       end
>>   end
=> nil
>> Stuff::Things.hello
=> "hello"
Nested modules
>> module Stuff
>>   module Things
>>       def self.hello
>>         "hello"
>>         end
>>       end
>>   end
=> nil
>> Stuff::Things.hello
=> "hello"
Nested modules
>> module Stuff
>>   module Things
>>       def self.hello
>>         "hello"
>>         end
>>       end
>>   end                  :: is for namespaces,
=> nil                    . is for method calls
>> Stuff::Things.hello
=> "hello"
include

• Modules are truly useful because you can
  “include” their methods into a class
• This is sometimes known as “mix-ins” —
  you mix the methods from a module into
  your current class
Inclusion rules

• “include” any defined module into any class
• The module is added to the ancestor
  chain, right above the class itself
• The module’s methods become instance
  methods for your class — because
  instances get methods from their class!
include
>> module Foo
>>   def yikes
>>     "yikes!"
>>     end
>>   end

>> class Person
>>   include Foo
>>   end

>> Person.new.yikes
  => "yikes!"
include
>> module Foo
>>   def yikes
>>     "yikes!"
>>     end
>>   end

>> class Person
>>   include Foo
>>   end

>> Person.new.yikes    Modules are objects,
  => "yikes!"
                      so you don’t use quotes
How does this work?
>> class Person
>>    end             >> class Person
   => nil             >>    include MyModule
                      >>    end
>> Person.ancestors      => Person
=> [Person, Object,
Wirble::Shortcuts,    >> Person.ancestors
PP::ObjectMixin,
                        => [Person, MyModule,
Kernel]
                      Object,
                      Wirble::Shortcuts,
>> module MyModule    PP::ObjectMixin,
>>    end             Kernel]
   => nil
How does this work?
    >> class Person
    >>    end             >> class Person
       => nil             >>    include MyModule
                          >>    end
    >> Person.ancestors      => Person
    => [Person, Object,
    Wirble::Shortcuts,    >> Person.ancestors
    PP::ObjectMixin,
                            => [Person, MyModule,
    Kernel]
                          Object,
                          Wirble::Shortcuts,
    >> module MyModule    PP::ObjectMixin,
MyModule was added to
    >>    end             Kernel]

  Person’s ancestors
       => nil
More than one include
>> Person.ancestors
   => [Person, MyModule, Object, Wirble::Shortcuts,
PP::ObjectMixin, Kernel]
>> module MyModule2
>>    end
   => nil
>> class Person
>>    include MyModule2
>>    end
   => Person
>> Person.ancestors
   => [Person, MyModule2, MyModule, Object,
    Wirble::Shortcuts, PP::ObjectMixin, Kernel]
More than one include
>> Person.ancestors
   => [Person, MyModule, Object, Wirble::Shortcuts,
PP::ObjectMixin, Kernel]
>> module MyModule2
>>    end
   => nil
>> class Person         include adds the new module
>>
>>
      include MyModule2
      end
                           right after the class itself
   => Person
>> Person.ancestors
   => [Person, MyModule2, MyModule, Object,
    Wirble::Shortcuts, PP::ObjectMixin, Kernel]
Implications

• Import as many modules as you want!
 • Sort of like multiple inheritance
• The most recent import “wins”
• Modules cannot override previously
  defined methods in a class
Enumerable

• The most popular mixin module is
  Enumerable
• If your class defines “each”, then you get a
  huge number of methods as a result
Class methods

• What if you want a module to define class
  methods?
• Import the module into the eigenclass, of
  course!
Regular include
>> module MyModule         >> i =
                           InstanceInclude.new
>>   def hello
                             => #<InstanceInclude:
>>     "hello"
                                0x103624220>
>>     end
                           >> i.hello
>>   end
                             => "hello"
  => nil
                           >> InstanceInclude.hello
>> class InstanceInclude
                           NoMethodError: undefined
>>   include MyModule
                           method `hello' for
>>   end                   InstanceInclude:Class
  => InstanceInclude               from (irb):11
Eigenclass include
>> class ClassInclude
>>    class << self
>>      include MyModule
>>      end
>>    end
   => #<Class:ClassInclude>
>> c = ClassInclude.new
   => #<ClassInclude:0x1035f20e0>
>> c.hello
   NoMethodError: undefined method `hello' for
#<ClassInclude:0x1035f20e0>
>> ClassInclude.hello
   => "hello"
Eigenclass include
>> class ClassInclude
>>    class << self
>>      include MyModule
>>      end
>>    end
   => #<Class:ClassInclude>

                    Here the eigenclass is “self,” so
>> c = ClassInclude.new
   => #<ClassInclude:0x1035f20e0>
>> c.hello          include imports the module as
                             class methods
   NoMethodError: undefined method `hello' for
#<ClassInclude:0x1035f20e0>
>> ClassInclude.hello
   => "hello"
extend

• Alternatively, we can use “extend,” which
  does the same thing — it includes the
  module on the eigenclass
• In other words, it imports the methods
  from the module as class methods
extend
>> class ExtendInclude
>>    extend MyModule
>>    end
   => ExtendInclude
>> e = ExtendInclude.new
   => #<ExtendInclude:0x1035d22b8>
>> e.hello
NoMethodError: undefined method `hello' for
#<ExtendInclude:0x1035d22b8>
          from (irb):24
>> ExtendInclude.hello
   => "hello"
Including and extending

• This is pretty magical, but useful
• When you include a module, the module’s
  “included” method is invoked — and the
  class that invoked it is passed as a
  parameter
included
>> module Squealer
>>    def self.included(base)
>>      puts "Squealer was just included by
'#{base.to_s}'!"
>>      end
>>    end
   => nil
>> class Includer
>>    include Squealer
>>    end
Squealer was just included by 'Includer'!
   => Includer
module Foo
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def bar
      puts 'class method'
    end
  end

  def foo
    puts 'instance method'
  end
end
lib in Rails
• All of the files in the “lib” directory are
  evaluated at startup time
• This means that you can define any number
  of modules in those files
• Put common functionality there — put
  common methods into a module, and then
  include those methods in your classes
irb addon: looksee
• gem install looksee
• require 'looksee/shortcuts' # in .irbrc
• “lp varname” shows you which methods
  came from which modules and classes
• Shows eigenclasses and metaclasses
• Useful and fun... but it apparently doesn’t
  work with jruby. Sigh.
Contacting me

• Call me in Israel: 054-496-8405
• Call me in the US: 847-230-9795
• E-mail me: reuven@lerner.co.il
• Interrupt me: reuvenlerner (Skype/AIM)

Ruby objects

  • 1.
    Objects in Ruby Reuven M. Lerner reuven@lerner.co.il September 21st, 2010
  • 2.
  • 3.
    Warning! • We’ll begetting into seriously hairy object stuff today. • This is both fun and useful, but can be tricky. • Please stop me and ask questions whenever you need!
  • 4.
    Dynamic typing • Rubyuses dynamic typing — meaning that variables don’t have types >> a = [1,2,3] => [1, 2, 3] >> a = 'hello' => "hello" >> a = {:a => 5, :b => 6} => {:b=>6, :a=>5}
  • 5.
    Strict typing • Butdata does have types, and those types are strictly enforced: >> puts 5 + "5" TypeError: String can't be coerced into Fixnum from (irb):30:in `+' from (irb):30 from :0
  • 6.
    Everything is anobject! • Numbers and Strings • Arrays and hashes • true, false, nil • Regular expressions, ranges • Files • Classes, Modules
  • 7.
    Object ID? • Everyobject in Ruby has an object ID, a unique integer • Builtin objects have permanent IDs • Objects that you define or create have their own object IDs • If the object IDs are equal, then we’re talking about the same object
  • 8.
    Object IDs >> a= [1,2,3] => [1, 2, 3] >> a.object_id => 2164172220 >> b = [1,2,3] => [1, 2, 3] >> b.object_id => 2164142300 >> a = b => [1, 2, 3] >> a.object_id - b.object_id => 0
  • 9.
    What is equality? •You can compare them with ==, to see if their contents are equal • You can compare their object IDs, to see if they point to the same piece of data
  • 10.
    That’s nice — butwhat is an object? • Every object is two things: • Some state (instance variables) • A pointer to a class
  • 11.
    OK, and whatis a class? • A class is four things: • Some state (because they’re objects) • A pointer to a class (because they’re objects) • A parent class • Zero or more methods
  • 12.
    Whoa! • A classis an object? • Yes. • What is its class? • Class. (Which is a constant identifier.) • So a class is an instance of Class? • Yes.
  • 13.
    Examples >> 60.class => Fixnum >> 60.class.class => Class >> "hello".class => String >> "hello".class.class => Class
  • 14.
    Let’s define aclass >> class MyClass >> end >> o = MyClass.new => #<MyClass:0x102ffdef0> >> o.class => MyClass
  • 15.
    Let’s define aclass >> class MyClass Class definition >> end >> o = MyClass.new => #<MyClass:0x102ffdef0> >> o.class => MyClass
  • 16.
    Let’s define aclass >> class MyClass Class definition >> end >> o = MyClass.new New object, class is MyClass => #<MyClass:0x102ffdef0> >> o.class => MyClass
  • 17.
    Let’s define aclass >> class MyClass Class definition >> end >> o = MyClass.new New object, class is MyClass => #<MyClass:0x102ffdef0> >> o.class See? I told you! => MyClass
  • 18.
    Methods • Methods aredefined on classes • When we invoke a method on object o, Ruby looks in o’s class (MyClass) for a method of that name • Ruby does this at runtime • Ruby has only one type of method! • No static, virtual, final, abstract, etc.
  • 19.
    Method example >> classMyClass >> def say_hello >> puts "hello" >> end >> end => nil >> o = MyClass.new => #<MyClass:0x1036160d0> >> o.say_hello hello => nil
  • 20.
    Method example >> classMyClass >> def say_hello >> puts "hello" Method definition >> end >> end => nil >> o = MyClass.new => #<MyClass:0x1036160d0> >> o.say_hello hello => nil
  • 21.
    Method example >> classMyClass >> def say_hello >> puts "hello" Method definition >> end >> end => nil >> o = MyClass.new New instance => #<MyClass:0x1036160d0> >> o.say_hello hello => nil
  • 22.
    Method example >> classMyClass >> def say_hello >> puts "hello" Method definition >> end >> end => nil >> o = MyClass.new New instance => #<MyClass:0x1036160d0> >> o.say_hello hello Invocation of method => nil
  • 23.
    No such method? •What if we invoke a method that doesn’t exist? >> o.say_goodbye NoMethodError: undefined method `say_goodbye' for #<MyClass: 0x1036160d0> from (irb):17
  • 24.
    method_missing >> class MyClass >> def method_missing(name) >> puts "You tried to execute the '#{name}' method. Too bad!" >> end >> end => nil >> o.say_goodbye You tried to execute the 'say_goodbye' method. Too bad! => nil
  • 25.
    method_missing >> class MyClass Symbol with method name >> def method_missing(name) >> puts "You tried to execute the '#{name}' method. Too bad!" >> end >> end => nil >> o.say_goodbye You tried to execute the 'say_goodbye' method. Too bad! => nil
  • 26.
    Method search order •Ruby looks for a matching method in the object’s class • If none is found, it tries in each ancestor class, in order • The class method “ancestors” returns an array of classes (and modules) • If no match found, invokes method_missing
  • 27.
    self • The “self”keyword is built into Ruby • It refers to the current object — the default receiver of messages • Knowing the value of “self” during each point of execution is important
  • 28.
    Inheritance! • Every objecthas a class • Every class (other than Object) has a superclass • We’ll look for methods in every parent class until we find a match or die trying
  • 29.
    Inheritance • When aclass is defined, we can set its superclass class MyClass # inherits from Object class MyClass < YourClass • Single inheritance only!
  • 30.
    Superclass • How canwe find the superclass? Ask the class! >> o.class => MyClass >> o.class.superclass => Object >> o.class.superclass.superclass => nil
  • 31.
    Methods are messages •When you say o.say_hello • You’re really invoking the “send” method: o.send(:say_hello)
  • 32.
    Mini-message sender >> defsend_a_message >> s = ('a'..'z').to_a.join >> print "Enter a method name: " >> puts s.send(gets.chomp.to_sym) >> end
  • 33.
    Method protection • Methodsare public by default • You may declare a method “public,” “private,” or “protected”
  • 34.
    public • Anyone caninvoke the method o.a_method o.send(:a_method) # same thing o.a_method(an_argument) o.send(:a_method, an_argument)
  • 35.
    private • Only selfcan invoke private methods • In other words, you cannot invoke self.method_name or o.method_name • And certainly not o.send(:method_name) • Only instances of the class in which it was defined (and its subclasses) may use it
  • 36.
    protected • Not commonlyused, in part because it’s hard to understand • Only if the sender and receiver of the message inherit the method from a common ancestor, may it be invoked
  • 37.
    Listing methods • Getan array of method names (as strings) o.methods o.public_methods # same as o.methods o.protected_methods o.private_methods
  • 38.
    What about data? •We said earlier that an object is data and a class pointer, and that methods sit on the class • So, where’s the data?
  • 39.
    Instance variables • Variablesthat are defined on an object, or “instance variables,” have a @ prefix • Often defined in the constructor • If accessed without being set, equal to nil • Unavailable outside of the object itself! • You must use methods to set or retrieve instance variables
  • 40.
    Instance variables >> o.instance_variables => ["@bar", "@foo"] >> o.instance_variable_get('@bar') => 2 >> o.instance_variable_set('@bar', 10) => 10 >> o.instance_variable_get('@bar') => 10
  • 41.
    Cool irb trick >>irb o >> instance_variables => ["@bar", "@foo"] >> @blah = 100 => 100 >> instance_variables => ["@blah", "@bar", "@foo"]
  • 42.
    Using instance variables class Person def initialize(first_name='', last_name='') @first_name = first_name @last_name = last_name end def name @first_name + ' ' + @last_name end end
  • 43.
    Defining getters • DefineVARNAME as a method: def first_name @first_name end puts o.first_name
  • 44.
    Defining setters • DefineVARNAME= as a method: def first_name=(value) @first_name = value end o.first_name = 5
  • 45.
    Attributes • Instead ofdefining setters and getters for all of your instance variables, create “attributes” attr_reader :foo # read-only attr_writer :bar # write-only attr_accessor :baz # read-write
  • 46.
    Attribute example class Person attr_accessor :first_name attr_accessor :last_name def initialize(first_name='', last_name='') @first_name = first_name @last_name = last_name end end
  • 47.
    Attribute example class Person attr_accessor :first_name attr_accessor :last_name def initialize(first_name='', last_name='') @first_name = first_name Still need to initialize @last_name = last_name our variables end end
  • 48.
    The story sofar • Data is private, stored in @variables • Access to data is handled via methods • Methods are on an object’s class, and can be kept private (but are public by default) • The first matching method in the class’s ancestry is invoked • No match? method_missing or exception
  • 49.
    Wait a second... •When I use ActiveRecord, I say Person.find(:all) • What does that mean? How does that fit into what we already know?
  • 50.
    Person.find • Person.find isa method • Methods are defined on the class of the object • Person is an instance of Class • So Person’s methods are defined in Class? • No, but that’s not a bad guess!
  • 51.
    Indeed, we cando that >> class Person >> end => nil >> Person.blah NoMethodError: undefined method `blah' for Person:Class from (irb):33 from :0
  • 52.
    Class methods >> classClass >> def blah >> puts "blah!" >> end >> end => nil >> Person.blah blah! => nil >> Fixnum.blah blah! => nil
  • 53.
    Class methods >> classClass Open up the Class class >> def blah >> puts "blah!" and define a method >> end >> end => nil >> Person.blah blah! => nil >> Fixnum.blah blah! => nil
  • 54.
    Class methods >> classClass Open up the Class class >> def blah >> puts "blah!" and define a method >> end >> end => nil >> Person.blah Yes, we can define blah! class methods this way => nil >> Fixnum.blah blah! => nil
  • 55.
    Class methods >> classClass Open up the Class class >> def blah >> puts "blah!" and define a method >> end >> end => nil >> Person.blah Yes, we can define blah! class methods this way => nil >> Fixnum.blah blah! Er, if we want them => nil to be defined for all classes
  • 56.
    Hmm. • Class methodsexist. • Methods are defined on an object’s class. • We know that Person.find isn’t defined on Class, because it is specific to ActiveRecord. • So where are class methods for Person defined?
  • 57.
    Singleton methods • Notthe “singleton” design pattern! • A very unfortunate term • Define a method on an object, not a class • Occasionally useful for specific needs • Important as a tool for understanding
  • 58.
    Singleton example >> a= "hello" >> def a.say_something(something) >> puts something >> end >> a.say_something("hello") => hello >> "another_string".say_something("hello") NoMethodError: undefined method `say_something' for "another_string":String from (irb):47 from :0
  • 59.
    Singleton example >> a= "hello" >> def a.say_something(something) >> puts something >> end >> a.say_something("hello") => hello >> "another_string".say_something("hello") NoMethodError: undefined method `say_something' for "another_string":String from (irb):47 from :0
  • 60.
    Singleton example >> a= "hello" >> def a.say_something(something) >> puts something >> end Notice the method is >> a.say_something("hello") defined on a! => hello >> "another_string".say_something("hello") NoMethodError: undefined method `say_something' for "another_string":String from (irb):47 from :0
  • 61.
    Singleton methods • Weadd a method to a particular instance • But wait — methods only exist on classes • What the heck is going on here?
  • 62.
    Eigenclasses Object String Class is String a
  • 63.
    Eigenclasses Object String Class is String a
  • 64.
    Eigenclasses Object String Class is String Eigenclass a
  • 65.
    Eigenclasses Object String Class is String Eigenclass New class in the middle a
  • 66.
    Eigenclasses Object String Class is String Eigenclass New class in the middle a Singleton method is defined here
  • 67.
    Eigenclasses?!? • Also knownas “ghost classes” — each object has its own eigenclass for private method storage • When you define a singleton method on an object, you’re creating an eigenclass that sits between the object and its class • Singleton methods are defined in the eigenclass
  • 68.
    Singleton override >> a= 'abc' >> a.reverse => "abc" => "ABC" >> b = 'def' >> b.reverse => "def" => "fed" >> def a.reverse >> b = a >> upcase => "abc" >> end >> b.reverse => nil => "ABC"
  • 69.
    Defining class methods •We define a singleton method by naming both the object and the method name: >> a = "hello" >> def a.say_something(something) >> puts something >> end
  • 70.
    Defining class methods •So we can define a class method by naming both the object and the method name: >> def Person.say_something(something) >> puts something >> end => nil >> Person.say_something('hello') hello => nil
  • 71.
    Instance vs. class methods • Rule: Methods for an object reside in its class • Methods for p (an instance of Person) are defined in Person • Methods for Person — class methods — are defined in Person’s eigenclass • Eigenclass of a class == “metaclass”
  • 72.
    Define class methods,1 >> class Person >> def Person.foo >> "foo" >> end >> end => nil >> Person.foo => "foo"
  • 73.
    Define class methods,1 >> class Person >> def Person.foo Defining on Person >> "foo" >> end >> end => nil >> Person.foo => "foo"
  • 74.
    Define class methods,2 >> class Person >> def self.bar >> "bar" >> end >> end => nil >> Person.bar => "bar"
  • 75.
    Define class methods,2 >> class Person >> def self.bar “self” in this >> "bar" context is Person >> end >> end => nil >> Person.bar => "bar"
  • 76.
    Define class methods,3 >> class Person >> class << self >> def baz >> "baz" >> end >> end >> end => nil >> Person.baz => "baz"
  • 77.
    Define class methods,3 >> class Person >> class << self Add to Person’s >> def baz eigenclass! >> "baz" >> end >> end >> end => nil >> Person.baz => "baz"
  • 78.
    Why do wecare? • Why should I care about eigenclasses? • How does this affect me? • Couldn’t you think of a sillier name?
  • 79.
    It’s really important •Once you understand eigenclasses, you basically understand the entire Ruby object model • There are no exceptions to this rule of how things work • Suddenly, instance methods, class methods, and modules fall into place
  • 80.
    Oh, and bythe way • We still haven’t explained how we defined Person.find • We know how to define a class method on a single class • But how do we define a class method for anything inheriting from ActiveRecord::Base?
  • 81.
    Answer • Define iton the eigenclass of the base class! >> class A >> class << self >> def boo >> "boo" >> end >> end >> end => nil
  • 82.
    That’s right: • OK,I admit it: Ruby is a bit sneaky here. • The superclass of the eigenclass of an object is the object’s class. • But the superclass of a class’s eigenclass is the eigenclass of the class’s superclass. • Or if you prefer: The superclass of the metaclass is the metaclass of the superclass.
  • 83.
    More on Eigenclasses Class Eigenclass of ActiveRecord::Base Class is Class Person
  • 84.
    More on Eigenclasses Class Class is Class Eigenclass of ActiveRecord::Base Person
  • 85.
    More on Eigenclasses Class Class is Class Eigenclass of ActiveRecord::Base Eigenclass Person
  • 86.
    More on Eigenclasses Class Class is Class Eigenclass of ActiveRecord::Base Eigenclass New class in the middle Person
  • 87.
    More on Eigenclasses Class Class is Class Eigenclass of ActiveRecord::Base Eigenclass New class in the middle Person “find” is defined here
  • 88.
    More on Eigenclasses Class Class is Class Eigenclass of ActiveRecord::Base Eigenclass New class in the middle Person “find” is defined here
  • 89.
    Unhiding eigenclasses class Object def eigenclass class << self self end end end
  • 90.
    Congratulations! • You’ve madeit through the hard part • Now we can move onto modules!
  • 91.
    Modules • Modules havetwo uses: • Namespaces • Keep related code together, for inclusion as a whole later on
  • 92.
    Modules vs. Classes •Both are capitalized (i.e., they’re constants) • Both can contain methods • Classes are modules (but modules are not classes) • They may contain constants • “self” inside refers to the module/class
  • 93.
    Modules vs. classes •Regular (instance) methods defined in a module cannot be accessed directly • Module methods defined in a module may be accessed just like class methods • They’re also defined just like class methods
  • 94.
    Module methods, 1 >>module Blah >> def Blah.foo >> "foo" >> end >> end => nil >> Blah.foo => "foo"
  • 95.
    Module methods, 1 >>module Blah >> def Blah.foo Defining on Blah >> "foo" >> end >> end => nil >> Blah.foo => "foo"
  • 96.
    Module methods, 2 >>module Blah >> def self.foo >> "foo" >> end >> end => nil >> Blah.foo => "foo"
  • 97.
    Module methods, 2 >>module Blah >> def self.foo self here is Blah >> "foo" >> end >> end => nil >> Blah.foo => "foo"
  • 98.
    Module methods, 3 >>module Blah >> class << self >> def baz >> "baz" >> end >> end >> end => nil >> Blah.baz => "baz"
  • 99.
    Module methods, 3 >>module Blah >> class << self Add to Blah’s >> def baz eigenclass! >> "baz" >> end >> end >> end => nil >> Blah.baz => "baz"
  • 100.
    Constants in modules >>Math::E => 2.71828182845905 >> Math::PI => 3.14159265358979 >> Math.PI NoMethodError: undefined method `PI' for Math:Module from (irb):99
  • 101.
    Constants in modules >>Math::E => 2.71828182845905 >> Math::PI => 3.14159265358979 >> Math.PI Methods aren’t constants! NoMethodError: undefined method `PI' for Math:Module from (irb):99
  • 102.
    Nested modules >> moduleStuff >> module Things >> def self.hello >> "hello" >> end >> end >> end => nil >> Stuff::Things.hello => "hello"
  • 103.
    Nested modules >> moduleStuff >> module Things >> def self.hello >> "hello" >> end >> end >> end => nil >> Stuff::Things.hello => "hello"
  • 104.
    Nested modules >> moduleStuff >> module Things >> def self.hello >> "hello" >> end >> end >> end :: is for namespaces, => nil . is for method calls >> Stuff::Things.hello => "hello"
  • 105.
    include • Modules aretruly useful because you can “include” their methods into a class • This is sometimes known as “mix-ins” — you mix the methods from a module into your current class
  • 106.
    Inclusion rules • “include”any defined module into any class • The module is added to the ancestor chain, right above the class itself • The module’s methods become instance methods for your class — because instances get methods from their class!
  • 107.
    include >> module Foo >> def yikes >> "yikes!" >> end >> end >> class Person >> include Foo >> end >> Person.new.yikes => "yikes!"
  • 108.
    include >> module Foo >> def yikes >> "yikes!" >> end >> end >> class Person >> include Foo >> end >> Person.new.yikes Modules are objects, => "yikes!" so you don’t use quotes
  • 109.
    How does thiswork? >> class Person >> end >> class Person => nil >> include MyModule >> end >> Person.ancestors => Person => [Person, Object, Wirble::Shortcuts, >> Person.ancestors PP::ObjectMixin, => [Person, MyModule, Kernel] Object, Wirble::Shortcuts, >> module MyModule PP::ObjectMixin, >> end Kernel] => nil
  • 110.
    How does thiswork? >> class Person >> end >> class Person => nil >> include MyModule >> end >> Person.ancestors => Person => [Person, Object, Wirble::Shortcuts, >> Person.ancestors PP::ObjectMixin, => [Person, MyModule, Kernel] Object, Wirble::Shortcuts, >> module MyModule PP::ObjectMixin, MyModule was added to >> end Kernel] Person’s ancestors => nil
  • 111.
    More than oneinclude >> Person.ancestors => [Person, MyModule, Object, Wirble::Shortcuts, PP::ObjectMixin, Kernel] >> module MyModule2 >> end => nil >> class Person >> include MyModule2 >> end => Person >> Person.ancestors => [Person, MyModule2, MyModule, Object, Wirble::Shortcuts, PP::ObjectMixin, Kernel]
  • 112.
    More than oneinclude >> Person.ancestors => [Person, MyModule, Object, Wirble::Shortcuts, PP::ObjectMixin, Kernel] >> module MyModule2 >> end => nil >> class Person include adds the new module >> >> include MyModule2 end right after the class itself => Person >> Person.ancestors => [Person, MyModule2, MyModule, Object, Wirble::Shortcuts, PP::ObjectMixin, Kernel]
  • 113.
    Implications • Import asmany modules as you want! • Sort of like multiple inheritance • The most recent import “wins” • Modules cannot override previously defined methods in a class
  • 114.
    Enumerable • The mostpopular mixin module is Enumerable • If your class defines “each”, then you get a huge number of methods as a result
  • 115.
    Class methods • Whatif you want a module to define class methods? • Import the module into the eigenclass, of course!
  • 116.
    Regular include >> moduleMyModule >> i = InstanceInclude.new >> def hello => #<InstanceInclude: >> "hello" 0x103624220> >> end >> i.hello >> end => "hello" => nil >> InstanceInclude.hello >> class InstanceInclude NoMethodError: undefined >> include MyModule method `hello' for >> end InstanceInclude:Class => InstanceInclude from (irb):11
  • 117.
    Eigenclass include >> classClassInclude >> class << self >> include MyModule >> end >> end => #<Class:ClassInclude> >> c = ClassInclude.new => #<ClassInclude:0x1035f20e0> >> c.hello NoMethodError: undefined method `hello' for #<ClassInclude:0x1035f20e0> >> ClassInclude.hello => "hello"
  • 118.
    Eigenclass include >> classClassInclude >> class << self >> include MyModule >> end >> end => #<Class:ClassInclude> Here the eigenclass is “self,” so >> c = ClassInclude.new => #<ClassInclude:0x1035f20e0> >> c.hello include imports the module as class methods NoMethodError: undefined method `hello' for #<ClassInclude:0x1035f20e0> >> ClassInclude.hello => "hello"
  • 119.
    extend • Alternatively, wecan use “extend,” which does the same thing — it includes the module on the eigenclass • In other words, it imports the methods from the module as class methods
  • 120.
    extend >> class ExtendInclude >> extend MyModule >> end => ExtendInclude >> e = ExtendInclude.new => #<ExtendInclude:0x1035d22b8> >> e.hello NoMethodError: undefined method `hello' for #<ExtendInclude:0x1035d22b8> from (irb):24 >> ExtendInclude.hello => "hello"
  • 121.
    Including and extending •This is pretty magical, but useful • When you include a module, the module’s “included” method is invoked — and the class that invoked it is passed as a parameter
  • 122.
    included >> module Squealer >> def self.included(base) >> puts "Squealer was just included by '#{base.to_s}'!" >> end >> end => nil >> class Includer >> include Squealer >> end Squealer was just included by 'Includer'! => Includer
  • 123.
    module Foo def self.included(base) base.extend(ClassMethods) end module ClassMethods def bar puts 'class method' end end def foo puts 'instance method' end end
  • 124.
    lib in Rails •All of the files in the “lib” directory are evaluated at startup time • This means that you can define any number of modules in those files • Put common functionality there — put common methods into a module, and then include those methods in your classes
  • 125.
    irb addon: looksee •gem install looksee • require 'looksee/shortcuts' # in .irbrc • “lp varname” shows you which methods came from which modules and classes • Shows eigenclasses and metaclasses • Useful and fun... but it apparently doesn’t work with jruby. Sigh.
  • 126.
    Contacting me • Callme in Israel: 054-496-8405 • Call me in the US: 847-230-9795 • E-mail me: reuven@lerner.co.il • Interrupt me: reuvenlerner (Skype/AIM)