Ruby Fundamentals - part 1 of 5

Classes, Modules, Monkey Patching and Refinements

·

3 min read

work in progress article

Classes

Ruby is a OOP (Object-Oriented Programming Language) and everything is an object, even nil, false and true.

But what about functional programming?

  • You can use the functional paradigm with any language.
  • Don't mix with Functional Programming Languages (Haskell, Erlang, Clojure...)
  • Some of the main differences between OOP and FP: Immutable data and Pure functions

| So, knowing that Ruby is a OOP, what happens when you call a method? Basically, Ruby will try to find the method in the current class, then will move up trying to find it.

  class Animal
    def name
      'animal'
    end
  end

  class Dog < Animal
  end

  puts Animal.new.name # => 'animal'
  puts Dog.new.name # => 'animal'

To check the parents classes of an object, there's the 'ancestors' method.

1.ancestors # => [Integer, Numeric, Comparable, Object, Kernel, BasicObject]

If even after traversing the whole stack up to BasicObject and does not find it you'll receive an Exception:

  10.hey # => NoMethodError (undefined method `hey' for 10:Integer)

In this case, the hey method was searched in the Integer, then Numeric and so on.

Ok, but how do I know and find where a method was defined?

  p Dog.new.method(:name).source_location # => ["file.rb", 2]

The method name was defined into the file.rb at line 2.

__

Modules

It's a way to componentize your methods. Let's say you have a Log module that creates some methods. You can just include this module inside other classes and these classes will be able to use the module methods.

Extend, Include and Prepend

You can load a module with this three forms.

  • include: instance methods
  • extend: class methods
  • prepend: upfront instance methods
module A
  def a
    'A'
  end
end

module B
  def b
    'B'
  end
end

module C
  def c
    'C'
  end
end

class Person
  include A
  extend B
  prepend C

  def c
    'called c at person class '
  end
end

puts Person.new.a # => 'A'
puts Person.b # => 'B'
puts Person.new.c # => 'C'

Monkey Patching and Refinements

=> Monkey Patching is the way to change and add methods inside classes.

class Integer
  def hey
    'hooyaa'
  end
end

10.hey # => "hooyaa"

It's a good practice? Well, I think so. Rails with ActiveSupport uses it quite a lot to add new methods in Ruby Core Classes, you can also use it to extend or change the behavior of a external gem.

 class Float
    def round
      'no'
    end
 end

 1.2.round # => "no"

What about Refinements?

=> As Monkey Patch scope is global, meaning it can break a lot of things if you're changing the default behavior of a method, Refinement is a way to limit the monkey patch.

module Rounded
  refine Float do
    def round
      'no'
    end
  end
end

class A
  def rounded
    1.5.round
  end

  using Rounded

  def refined_rounded
    1.5.round
  end
end

puts A.new.rounded # => 2
puts A.new.refined_rounded # => no