Presented by-
ADITYA TIWARI
Ruby - Diving Deep
Know what you know...
3 Pillars of ruby
1. Everything is an object
2. Every operation is a method call on some object.
3. Everything is metaprogramming.
1. Everything is an object(Almost)
• Even lowly integers and nil are true objects:
57.to_s
57.methods
57.heinz_varieties
nil.respond_to?(:to_s)
1 + 2 => 1.+(2) or 1.send(:+, 2)
my_array[4] => my_array.[](4)
or my_array.send(:[], 4)
my_array[3] = "foo" => my_array.[]=(3, ‘foo’)
if (x == 3) .... => if (x.==(3))
my_func(z) => self.my_func(z)
or self.send(:my_func, z)
2. Everything is a mETHOD CALL
Example: (Operator overloading)
1 + 2 3
‘a’ + ‘b’ ‘ab’
[1] + [2] [1, 2]
Numeric#+, String#+, Array#+
So, can I have my own version of + method?
Dynamically typed: objects have types; variables don’t
1. 2.class > Integer
2. That’s why variables always need to be initialized.
3. Everything is pass by reference (except primitive data
types like integer, float, symbol because they take very
little space and they are immutable)
MetaProgramming and reflection
1. Reflection lets us ask an object questions about itself
and have it modify itself.
2. Metaprogramming lets us define new code at runtime.
3. How can these make our code DRYer, more concise, or
easier to read? – (or are they just twenty-dollar words
to make me look smart?)
Example: An international bank account - Open
classes
acct.deposit(100) # deposit $100
acct.deposit(euros_to_dollars(20)) # deposit euro
acct.deposit(CurrencyConverter.new( :euros, 20))
acct.deposit(100) # deposit $100
acct.deposit(20.euros) # about $25
● No problem with open classes....
class Numeric
def euros ; self * 1.292 ; end
end
● But what about
acct.deposit(1.euro)
The power of method_missing
● But suppose we also want to support
acct.deposit(1000.yen)
acct.deposit(3000.rupees)
• Surely there is a DRY way to do this?
https://pastebin.com/agjb5qBF
1. # metaprogramming to the rescue!
2.
3. class Numeric
4. @@currencies = {'yen' => 0.013, 'euro' => 1.292, 'rupee' => 0.019}
5. def method_missing(method_id, *args, &block) # capture all args in case have to call super
6. singular_currency = method_id.to_s.gsub( /s$/, '')
7. if @@currencies.has_key?(singular_currency)
8. self * @@currencies[singular_currency]
9. else
10. super
11. end
12. end
13. end
Inheritanc( Diving deep)
super
allows us to call methods up the inheritance
hierarchy.
Some facts about super
1. Super automatically forwards the params to it’s parent’s
methods.
2. You can prevent this behaviour by explicitly passing the
desired params or no params.
Example super()
Modules
Important use of modules:
1. mix its methods into a class:
class A ; include MyModule ; end
What if method is defined in multiple places?
– A.foo will search A, then MyModule, then
method_missing in A, then A's ancestor
– sort is actually defined in module Enumerable, which
is mixed into Array by default.
Include vs extend
Include: Add module’s methods as instance methods.
Extend: Add module’s methods as class methods.
A Mix-in is a Contract
Example:
● Enumerable assumes target object responds to each
– ...provides all?, any?, collect, find, include?,
inject, map, partition, ....
• Enumerable also provides sort, which requires elements of
collection (things returned by each) to respond to <=>
• Comparable assumes that target object responds to
<=>(other_thing)
Module for namespacing
module Predator
class Tiger
end
End
Predator::Tiger
When Module? When Class?
• Modules reuse behaviors
– high-level behaviors that could conceptually apply to
many classes – Example: Enumerable, Comparable
– Mechanism: mixin (include Enumerable)
• Classes reuse implementation
– subclass reuses/overrides superclass methods
– Mechanism: inheritance (class A < B)
VS java
ArrayList aList;
Iterator it = aList.iterator();
while (it.hasNext()) {
Object element = it.getNext();
// do some stuff with element
}
• Goal of the code: do stuff with elements of aList
Block?
[1, 2, 3].each do |i|
i.to_s
end
OR
[1, 2, 3].each { |i| i.to_s }
Example
Turning iterators inside-out
• Java:
– You hand me each element of that collection in turn.
– I’ll do some stuff.
– Then I’ll ask you if there’s any more left.
• Ruby:
– Here is some code to apply to every element of the
collection.
– You manage the iteration or data structure traversal.
And give me result.
Blocks are Closures
• A closure is the set of all variable bindings you can “
see ” at a given point in time
– In Scheme, it’s called an environment
• Blocks are closures: they carry their environment around
with them
• Result: blocks can help reuse by separating what to do
from where & when to do it
Advance metaprogramming
Adding methods in the context of an object
1. Is it possible to call private method of an object from
outside the class?
2. Declaring the class methods is the classical example of
adding methods to the objects.
closure
Block carries it’s local environment with it. That’s called
closure.
Yield(advance)
1. Yield with arguments:
def calculation(a, b)
yield(a, b)
end
puts calculation(5, 6) { |a, b| a + b } # addition
puts calculation(5, 6) { |a, b| a - b } # subtraction
Yield is neither an object nor a method
def foo
puts yield
puts method(:foo)
puts method(:yield)
end
foo { "I expect to be heard." }
Is it given
Block_given?
def foo
yield
end
Foo # LocalJumpError
Another way to invoke block
def what_am_i(&block)
block.class
end
puts what_am_i {} #Proc
A block is just a Proc! That being said, what is a Proc?
Procedures, AKA, Procs
class Array
def iterate!(code)
self.each_with_index do |n, i|
self[i] = code.call(n)
end
end
end
array_1 = [1, 2, 3, 4]
array_2 = [2, 3, 4, 5]
square = Proc.new do |n|
n ** 2
end
array_1.iterate!(square)
array_2.iterate!(square)
More example
def callbacks(procs)
procs[:starting].call
puts "Still going"
procs[:finishing].call
end
callbacks(:starting => Proc.new { puts "Starting" },
:finishing => Proc.new { puts "Finishing" })
Lambda the anonymous function
class Array
def iterate!(code)
self.each_with_index do |n, i|
self[i] = code.call(n)
end
end
end
array = [1, 2, 3, 4]
array.iterate!(lambda { |n| n ** 2 })
puts array.inspect
Lambda is just like procs so what’s the difference?
1. unlike Procs, lambdas check the number of arguments
passed.
def args(code)
one, two = 1, 2
code.call(one, two)
end
args(Proc.new{|a, b, c| puts "Give me a #{a} and a #{b} and a #{c.class}"})
args(lambda{|a, b, c| puts "Give me a #{a} and a #{b} and a #{c.class}"})
2. A Proc return will stop a method and return the value
provided, lambdas will return their value to the method and
let the method continue on, just like any other method.
def proc_return
Proc.new { return "Proc.new"}.call
return "proc_return method finished"
end
def lambda_return
lambda { return "lambda" }.call
return "lambda_return method finished"
end
puts proc_return
puts lambda_return
If lambda are methods so can we
store existing methods and pass them
just like Procs?
def iterate!(code)
self.each_with_index do |n, i|
self[i] = code.call(n)
end
end
end
def square(n)
n ** 2
end
array = [1, 2, 3, 4]
array.iterate!(method(:square))
puts array.inspect

Ruby basics

  • 1.
    Presented by- ADITYA TIWARI Ruby- Diving Deep Know what you know...
  • 2.
    3 Pillars ofruby 1. Everything is an object 2. Every operation is a method call on some object. 3. Everything is metaprogramming.
  • 3.
    1. Everything isan object(Almost) • Even lowly integers and nil are true objects: 57.to_s 57.methods 57.heinz_varieties nil.respond_to?(:to_s)
  • 4.
    1 + 2=> 1.+(2) or 1.send(:+, 2) my_array[4] => my_array.[](4) or my_array.send(:[], 4) my_array[3] = "foo" => my_array.[]=(3, ‘foo’) if (x == 3) .... => if (x.==(3)) my_func(z) => self.my_func(z) or self.send(:my_func, z) 2. Everything is a mETHOD CALL
  • 5.
    Example: (Operator overloading) 1+ 2 3 ‘a’ + ‘b’ ‘ab’ [1] + [2] [1, 2] Numeric#+, String#+, Array#+ So, can I have my own version of + method?
  • 6.
    Dynamically typed: objectshave types; variables don’t 1. 2.class > Integer 2. That’s why variables always need to be initialized. 3. Everything is pass by reference (except primitive data types like integer, float, symbol because they take very little space and they are immutable)
  • 7.
    MetaProgramming and reflection 1.Reflection lets us ask an object questions about itself and have it modify itself. 2. Metaprogramming lets us define new code at runtime. 3. How can these make our code DRYer, more concise, or easier to read? – (or are they just twenty-dollar words to make me look smart?)
  • 8.
    Example: An internationalbank account - Open classes acct.deposit(100) # deposit $100 acct.deposit(euros_to_dollars(20)) # deposit euro acct.deposit(CurrencyConverter.new( :euros, 20))
  • 9.
    acct.deposit(100) # deposit$100 acct.deposit(20.euros) # about $25 ● No problem with open classes.... class Numeric def euros ; self * 1.292 ; end end ● But what about acct.deposit(1.euro)
  • 10.
    The power ofmethod_missing ● But suppose we also want to support acct.deposit(1000.yen) acct.deposit(3000.rupees) • Surely there is a DRY way to do this? https://pastebin.com/agjb5qBF
  • 11.
    1. # metaprogrammingto the rescue! 2. 3. class Numeric 4. @@currencies = {'yen' => 0.013, 'euro' => 1.292, 'rupee' => 0.019} 5. def method_missing(method_id, *args, &block) # capture all args in case have to call super 6. singular_currency = method_id.to_s.gsub( /s$/, '') 7. if @@currencies.has_key?(singular_currency) 8. self * @@currencies[singular_currency] 9. else 10. super 11. end 12. end 13. end
  • 12.
  • 13.
  • 14.
    allows us tocall methods up the inheritance hierarchy.
  • 15.
    Some facts aboutsuper 1. Super automatically forwards the params to it’s parent’s methods. 2. You can prevent this behaviour by explicitly passing the desired params or no params. Example super()
  • 16.
    Modules Important use ofmodules: 1. mix its methods into a class: class A ; include MyModule ; end What if method is defined in multiple places?
  • 17.
    – A.foo willsearch A, then MyModule, then method_missing in A, then A's ancestor – sort is actually defined in module Enumerable, which is mixed into Array by default.
  • 18.
    Include vs extend Include:Add module’s methods as instance methods. Extend: Add module’s methods as class methods.
  • 19.
    A Mix-in isa Contract Example: ● Enumerable assumes target object responds to each – ...provides all?, any?, collect, find, include?, inject, map, partition, .... • Enumerable also provides sort, which requires elements of collection (things returned by each) to respond to <=> • Comparable assumes that target object responds to <=>(other_thing)
  • 20.
    Module for namespacing modulePredator class Tiger end End Predator::Tiger
  • 21.
    When Module? WhenClass? • Modules reuse behaviors – high-level behaviors that could conceptually apply to many classes – Example: Enumerable, Comparable – Mechanism: mixin (include Enumerable) • Classes reuse implementation – subclass reuses/overrides superclass methods – Mechanism: inheritance (class A < B)
  • 22.
    VS java ArrayList aList; Iteratorit = aList.iterator(); while (it.hasNext()) { Object element = it.getNext(); // do some stuff with element } • Goal of the code: do stuff with elements of aList
  • 23.
    Block? [1, 2, 3].eachdo |i| i.to_s end OR [1, 2, 3].each { |i| i.to_s }
  • 24.
  • 25.
    Turning iterators inside-out •Java: – You hand me each element of that collection in turn. – I’ll do some stuff. – Then I’ll ask you if there’s any more left. • Ruby: – Here is some code to apply to every element of the collection. – You manage the iteration or data structure traversal. And give me result.
  • 26.
    Blocks are Closures •A closure is the set of all variable bindings you can “ see ” at a given point in time – In Scheme, it’s called an environment • Blocks are closures: they carry their environment around with them • Result: blocks can help reuse by separating what to do from where & when to do it
  • 27.
  • 28.
    Adding methods inthe context of an object 1. Is it possible to call private method of an object from outside the class? 2. Declaring the class methods is the classical example of adding methods to the objects.
  • 29.
    closure Block carries it’slocal environment with it. That’s called closure.
  • 30.
    Yield(advance) 1. Yield witharguments: def calculation(a, b) yield(a, b) end puts calculation(5, 6) { |a, b| a + b } # addition puts calculation(5, 6) { |a, b| a - b } # subtraction
  • 31.
    Yield is neitheran object nor a method def foo puts yield puts method(:foo) puts method(:yield) end foo { "I expect to be heard." }
  • 32.
    Is it given Block_given? deffoo yield end Foo # LocalJumpError
  • 33.
    Another way toinvoke block def what_am_i(&block) block.class end puts what_am_i {} #Proc A block is just a Proc! That being said, what is a Proc?
  • 34.
    Procedures, AKA, Procs classArray def iterate!(code) self.each_with_index do |n, i| self[i] = code.call(n) end end end array_1 = [1, 2, 3, 4] array_2 = [2, 3, 4, 5] square = Proc.new do |n| n ** 2 end array_1.iterate!(square) array_2.iterate!(square)
  • 35.
    More example def callbacks(procs) procs[:starting].call puts"Still going" procs[:finishing].call end callbacks(:starting => Proc.new { puts "Starting" }, :finishing => Proc.new { puts "Finishing" })
  • 36.
    Lambda the anonymousfunction class Array def iterate!(code) self.each_with_index do |n, i| self[i] = code.call(n) end end end array = [1, 2, 3, 4] array.iterate!(lambda { |n| n ** 2 }) puts array.inspect
  • 37.
    Lambda is justlike procs so what’s the difference? 1. unlike Procs, lambdas check the number of arguments passed. def args(code) one, two = 1, 2 code.call(one, two) end args(Proc.new{|a, b, c| puts "Give me a #{a} and a #{b} and a #{c.class}"}) args(lambda{|a, b, c| puts "Give me a #{a} and a #{b} and a #{c.class}"})
  • 38.
    2. A Procreturn will stop a method and return the value provided, lambdas will return their value to the method and let the method continue on, just like any other method. def proc_return Proc.new { return "Proc.new"}.call return "proc_return method finished" end def lambda_return lambda { return "lambda" }.call return "lambda_return method finished" end puts proc_return puts lambda_return
  • 39.
    If lambda aremethods so can we store existing methods and pass them just like Procs?
  • 40.
    def iterate!(code) self.each_with_index do|n, i| self[i] = code.call(n) end end end def square(n) n ** 2 end array = [1, 2, 3, 4] array.iterate!(method(:square)) puts array.inspect