A limited guide to intermediate and
advanced Ruby
- Vysakh Sreenivasan (vysakh0)
Variables store only
references to objects.
So, use dup or clone
scan & match
match returns the first match.
scan returns all matches
(g - global)
gsub and sub
>> "hello".sub('l', '*')
=> "he*lo"
>> "hello".gsub('l', '*')
=> "he**o"
(when working in irb or pry)
semicolon ;
[82] pry(main)> a = [1, 2, 3]
=> [1, 2, 3]
[83] pry(main)> a = [1, 2, 3] ;
[84] pry(main)>
Does not print output of the statement
Spaceship operator
<=>
a <=> b :=
if a < b then return -1
if a = b then return 0
if a > b then return 1
if a and b are not comparable then return
nil
[1,3,2] <=> [2,2,2]
- Ruby will start comparing each element of
both array from left hand side.
- 1 of left array is smaller than 2 of right
array.
- Hence left array is smaller than right array.
Output will be -1.
case subsumption operator or case
equality operator
===
- "If I have a drawer labelled a would it
make sense to put b in that drawer?"
- The main usage for the === operator is in
case expressions
a === b
case a
when b
end
# Here b === a check is made in when
# not b == a
Diff between a == b and a === b
- (a == b) and (b == a) is always the same
- (a === b) and (b === a) is not always the
same
Integer === 2 #true
2 === Integer #false
splat
Accept variable length arguments
Unwrap from array and pass as args
Coerce values into array
coerced = *"Hello World"
p coerced
# => ["Hello World"]
coerced = *nil
p coerced
# => []
coerced = *(1..3)
p coerced
# => [1, 2, 3]
Deconstruct array
data = [1, 2, 3, 4]
first, *rest = data
puts "#{first}, #{rest}"
# => 1, [2, 3, 4]
*list, last = data
puts "#{list}, #{last}"
# => [1, 2, 3], 4
first, *center, last = data
puts "#{first}, #{center}, #{last}"
# => 1, [2, 3], 4
Double Splat
Similar to splat but for hashes in arg list
def double_splat(**hash)
p hash
end
double_splat()
# => {}
double_splat(a: 1)
# => {:a => 1}
double_splat(a: 1, b: 2)
# => {:a => 1, :b => 2}
def foo (args)
return args
end
h1 = { b: 2 }
h2 = { c: 3 }
foo(a: 1, **h1) # => {:a=>1, :b=>2}
foo(a: 1, **h1, **h2) # => {:a=>1, :b=>2, :c=>3}
foo(a: 1, h1) # Syntax Error: syntax error, unexpected ')', expecting =>
foo(h1, h2) # ArgumentError: wrong number of arguments (2 for 1)
We just used double splat to capture all
keyword arguments
def join_array(arr, elements: , by_char: ‘,’)
arr.take(elements).join(by_char)
end
a = [20, 30, 10, 40]
# optional if there is default set in parameters, in this case it is by_char
p join_array(a, elements: 3)
p join_array(a, elements: 2, by_char: ‘-’)
# order not important
p join_array(a, by_char: ‘-’, elements: 3)
Damn! lambda is not rhyming! :/
blocks, procs and lambdas
Just passing around a chunk of code
def parrot(&what_human_said)
puts “Hey, I’m gonna say what you said”
what_human_said.call
puts “Thank you. Have a nice day”
end
parrot do
puts “I’m not Siri”
puts “I’m green, eeee”
end
&what_human_said
makes passed block changed to proc
Sorta like a magic :)
Ruby also lets us do it implicitly
def parrot
puts “Hey, I’m gonna say what you said”
yield
puts “Thank you. Have a nice day”
end
parrot do
puts “I’m not Siri”
puts “I’m green, eeee”
end
yield
- is not a method but a type.
- yield executes the block.
- faster than previous eg.
block_given? method of ruby
But, what is proc?
- an object
- with blocks of code
- bound to a set of local variables
lambda is just like proc but strict and nice
- lambda checks parameters passed to it
- lambda is not like that adamant child that
forces you out of the movie just because
he didn’t like it.
Symbol#to_proc
odd = proc { |x| x.odd? }
=> #<Proc:0xb8c1c6d8>
odd.call(2)
=> false
odd.call(3)
=> true
odd = :odd?.to_proc
=> #<Proc:0xb8c1c6d8>
odd.call(2)
=> false
odd.call(3)
=> true
Both these are equivalent
:odd?.to_proc #==> &:odd?
proc { |x| x.odd? }
Also note, these are same
1 + 2
#=> 3
1.send(:+, 2)
#=> 3
And so, this works too(equivalence)
:odd?.to_proc
proc { |x| x.send(:odd?) }
And hence, these are same :)
[“ruby”, “rails”, “thor”].map do |x|
x.reverse
end
[“ruby”, “rails”, “thor”].map(&:reverse)
Proc=== is an alias to Proc.
call
mul_of_3 = proc { |num| num % 3 == 0 }
mul_of_3.call(30) #=> true
mul_of_3 === 30 #=> true
# And so,
case 30
when mul_of_3
puts “Yayyy”
end
#=> Yayyy
Proc curry
- A curried proc receives some arguments.
- If a sufficient number of arguments are
supplied, it passes the supplied arguments
to the original proc and returns the result.
multiple = proc { |x, y| x * y }
multiple.call(2, 3) #=> 6
# here x = 2, y = 3
curry_multiple = multiple.curry
#=> curried the multiple proc
multiple_of_2 = curry_multiple.call(2)
# here x is 2, y is not yet passed
# when y is passed, the proc will
# get executed
multiple_of_2.call(3) #=> 6
# here x is 2, y is 3
multiply = proc { |*args| args.reduce(:*) }
multiply_2_nums = multiply.curry(2)
twice= multiply_2_nums.call(2)
twice.call(5) # => 10
methods
Assert if method exists
class Twitter
def tweet; end
end
t = Twitter.new
t.respond_to?(:tweet)
Twitter.method_defined?(:tweet)
making method object
def sum(a, b)
a + b
end
s = method(:sum)
s.methods # s is an object now :D
s.parameters
s.super_method
s.call(10, 20)
unbound method object
class Calc
def sum(a, b)
a + b
end
end
s = Calc.instance_method(:sum)
s.call(10, 20) # NoMethodError
s.bind(Calc.new).call(10, 20)
def palindrome?
self.reverse == self
end
pal = Object.instance_method :palindrome?
=> #<UnboundMethod: Object#palindrome?>
pal.bind("Madam").call
=> false
pal.bind("madam").call
=> true
You can also invoke a method by
s.send(:sum, 10, 20)
eval “s.sum(1, 2)”
# eval is not recommended, never use
defining methods
(different way)
class Order
define_method :total_for |quantity|
10 * quantity
end
end
Order.new.total_for(20)
#=> 200
undefining methods
class Order
define_method :total_for |quantity|
10 * quantity
end
undef_method :total_for
#just unlinks but is in memory
end
class Order
define_method :total_for |quantity|
10 * quantity
end
remove_method :total_for
#removes it totally
end
alias method
class Politics
def free_scheme
puts “Yo. Get lots of free stuff.”
end
alias_method :money_time, :free_scheme
end
Politics.new.free_scheme # what you see :P
Politics.new.money_time # what they see :P
method_missing
(Do not try this at your home/code)
not found is not an option for you
Use it only when method
method curry
def multiple(x, y)
x * y
end
curry_multiple = method(:multiple).curry
#=> curried the multiple method, got proc
twice = curry_multiple.call(2)
twice.call(3) #=> 6
Monkey patch
Open up classes and insert your own methods
class A
end
class A
def hello
“yolo”
end
end
class String
def palindrome?
self.reverse == self
end
end
# adds a method to the already existing String Class
Refinements
module Watson
refine String do
def palindrome?
self.reverse == self
end
end
end
“Ruby”.palindrome?
#=> NoMethodError: undefined method `palindrome?' for "Ruby":String
#Not available outside the namespace
module Watson
p “RoR”.palindrome?
end
module Sherlock
using Watson
p “RoR”.palindrome?
end
Objects
inherits from the class “Object”
Every object in Ruby by default
class Foo
end
class Foo < Object
end
Both these classes are identical
Even class is an object
Foo.is_a?(Object)
Foo.new.is_a?(Object)
Both these are Objects
Ancestry
class Elizabeth1; end
class Victoria < Elizabeth1
end
class Elizabeth2 < Victoria
end
Who was Elizabeth1’s
ancestor?
May be just another
human?
Then who was human’s
ancestor?
Some ape, duh!
The APE is the BasicObject
In Ruby, human is Object
Who is this kernel?
And there are qualities that got mixed into you
See, you are human, not ape
that is mixed into the “Object” class
Kernel is a module
Ya, modules are part of
ancestors
Singleton methods
And how did you define on an object?
But only classes has methods
a singleton class(invisible to you) for the object
You are right. Ruby makes
just to hold object specific methods
have singleton classes not only objects
You know what, even classes
class God
def self.true_me
“:troll_face:”
end
end
God.singleton_class
God.singleton_methods
God.singleton_method(:true_me).call
God.true_me
singleton class for the class just to hold
So, ruby makes a
class specific methods ?
Foo and Foo.new are objects
Wait, I remember you saying
the class to hold the class specific methods
So, actually ruby is making a singleton class for
just like it does for object because class is
also object?
invisible to the programmer.
AH! Yes! And The singleton class is typically
But you sorta see in one syntax, which is
class God
class << self
def self.true_me
“:troll_face:”
end
end
end
class << self is like class Singleton(self)
- You want to add a method just to a specific
object. (note: even class is an object)
- But you can’t write method to a object.
- So, you write method to object’s singleton
class like this.
class Human;end
matz = Human.new
class << matz
def write
puts “Writing the epic: Ruby”
end
end
matz.write
You can also define
Singleton methods by
class RubyLang; end
RubyLang.define_singleton_method(:minaswan) do
puts “Matz is nice and so we are nice”
end
RubyLang.minaswan
Ruby OO goodies
Yo = Class.new do
def ping
“hodor”
end
end
Yo.new.ping
A = Class.new
# proper class but..
a = Class.new
# an anonymous generic class
Struct is like class
with bundled accessor methods
Product = Struct.new(:name, :price)
pr = Product.new(“Book”, 20)
pr.name # attr_accessor out of box
pr.price = 10
pr.to_h #=> {:name=>”Book”, :price => 10}
a = *pr #=> [”Book”, 10]
Product = Struct.new(:name, :price) do
def title
“#{name} #{price}”
end
end
OpenStruct is like Struct
with more manipulation as if it is a hash
require ‘ostruct’
param = { name: “book”, price: 20 }
param_obj = OpenStruct.new(param)
param_obj.delivered = false
param_obj[:discount] = 5
param_obj.delete_field :delivered
param_obj.to_h
Delegator
Inherit it, and don’t write constructor as it
is already defined.
class CEO
attr_accessor :next_appointment
def fix_appointment(date)
next_appoinment = date
end
end
Ceo decides to hire a PA
and delegate some of her work to PA
require ‘delegate’
class CEO
attr_accessor :next_appointment
end
class PA < SimpleDelegator
def fix_appointment(date)
next_appoinment = date
end
end
elon_musk = CEO.new
elon_musk_pa = PA.new(elon_musk)
elon_musk_pa.fix_appointment(“7/6/2015”)
# you won’t know anything else about the ceo
Forwardable
require ‘forwardable’
class CEO
attr_accessor :availability, :office_mails
def phone_call
puts “Hi. How are you? How can I help you?”
yield
end
end
class PA
extend Forwardable
def_delegators :@ceo, :availability, :phone_call, :office_mails
def initialize(ceo)
@ceo = ceo
end
def check_my_mail(mail)
office_mails.include? (mail)
end
end
jack_ma = CEO.new(“Jack Ma”)
jack_ma_pa = PA.new(jack_ma)
jack_ma_pa.phone_call do
puts “Hello, mr.jack ma.”
end
jack_ma_pa.check_my_mail(1234)
Also look for other awesome classes
- Queue
- Stack
- Thread
- StringScanner
Manipulating variables
dynamically
class A
def initialize(foo)
@foo = foo
end
end
a = A.new (“cool”)
a.instance_variable_get “foo”
a.instance_variable_set :@foo, “hotl”
a.instance_variables
class A
@@b = 10
end
A.class_variable_get “b”
A.class_variables
const_get
class A
PI = 3.14
end
A.const_get “PI” # also there is const_set
#only works with the classes not with objects
Eval
The evil }:-D
(Ruby will run this string )
eval “puts ‘hello world’ “
instance_eval
evaluates string/block of code in the
context of the object
class Subject
def initialize(name)
@name
end
end
maths = Subject.new(“Mathematics”)
maths.name #=> ERROR
maths.instance_eval { @name } #=> “Mathematics”
# earlier we saw this code
class Human;end
matz = Human.new
class << matz
def write
puts “Writing the epic: Ruby”
end
end
matz.write
# Can also be written as
class Human;end
matz = Human.new
matz.instance_eval do
def write
puts “Writing the epic: Ruby”
end
end
matz.write
Since class is also an object
we can use instance_eval on it.
# we earlier saw this
class RubyLang; end
RubyLang.define_singleton_method(:minaswan) do
puts “Matz is nice and so we are nice”
end
RubyLang.minaswan
# Also can be written as
class RubyLang; end
RubyLang.instance_eval do
puts “Matz is nice and so we are nice”
end
RubyLang.minaswan
class_eval
evaluates string/block of code in the
context of the class
class Person; end
Person.new.name #=> ERROR
Person.class_eval do
attr_accessor :name
def trumpet
puts “Me..me...me… #{name}”
end
end
person = Person.new
person.name = “Narcissis”
person.trumpet
Modules
collection of methods and constants
module Mod
CONST = 1
def math
# ...
end
end
Mod.class #=> Module
Mod.constants #=> [:CONST]
Mod.instance_methods #=> [:math]
that you can use across multiple classes
Modules are about providing methods
- extend
- include
- prepend
- using (refinements)
We already saw about ‘using’
module Hipster
def flame_wars
puts “Yo, this lang is better, that lang is worse”
end
end
class Programmer
end
me = Programmer.new
me.extend Hipster
me.flame_wars
Can you guess where the
module methods were inserted for object?
extend just did that for us :)
To the singleton class of the object.
module Hipster
def flame_wars
puts “Yo, this lang is better, that lang is worse”
end
end
class HipsterProgrammer
extend Hipster
end
HipsterProgrammer.flame_wars
Yes. We inserted module methods to
the singleton class of class by using extend
What if we want to just insert
module methods to the class?
module Hipster
def flame_wars
puts “Yo, this lang is better, that lang is worse”
end
end
class HipsterProgrammer
include Hipster
end
me = HipsterProgrammer.new
me.flame_wars
class HipsterProgrammer
include Hipster
end
HipsterProgrammer.ancestors
# => [HipsterProgrammer, Hipster, …]
# NOTE: You are not inheriting here, but you are changing
# method lookup chain here
module Validations
def save
puts "validating model"
end
end
class Model
include Validations
def save
super
puts "saving model"
end
end
m = Model.new
m.save
#=> validating model
#=> saving model
prepend is just like include
But prepends itself first in the ancestors chain
module Friend
def opinion
puts "This would be best for you :) .."
end
end
class NormalPerson
include Friend
def opinion
puts "I think this would be best for me"
end
end
NormalPerson.ancestors
#=> [NormalPerson, Friend,...]
# This is the order of method look up
NormalPerson.new.opinion
#=> “I think this would be best for me”
- Though “opinion” method exists in both
classes
- it would start from the first of the ancestors
- i.e NormalPerson in our case
module Friend
def opinion
puts "This would be best for you :) .."
end
end
class BFF
prepend Friend
def opinion
puts "I think this would be best for me"
end
end
BFF.ancestors
#=> [Friend, BFF, ...]
# This is the order of method look up
BFF.new.opinion
#=> “This would be best for you :) ..”
double underscore basically indicates
"special/avoid overwrite”
__VALUE__
- In foo.rb, __FILE__ would be "foo.rb".
- If foo.rb were in the dir /home/vysakh
then File.dirname(__FILE__) would return
/home/vysakh.
__FILE__
if foo.rb were in the dir /home/vysakh then
__dir__ would return /home/vysakh.
__dir__
def foo
[__method__, __callee__]
end
alias bar foo
p foo #=> [:foo, :foo]
p bar #=> [:foo, :bar]
__callee__ and __method__
class Errand
def send
puts “Buy stuff”
end
def check
puts “checking…”
end
end
Errand.new.__send__(“check”)
use __send__ if the name send clashes with an existing method in obj
Also there are
__ENCODING__ (keyword)
__LINE__ (keyword)
__getobj__ (SimpleDelegator)
__setobj__ (SimpleDelegator)
Things we missed
- Operators like &&=, ||=, =~
- File operations
- System calls
- Enumerator methods
- threads
- exceptions, backtraces, benchmarking
- and many more, do check it out
Keep calm and write
Prose and Poetry using Ruby :)

A limited guide to intermediate and advanced Ruby