Examples: https://gist.github.com/aditya01933/c6a867e981110885369f06c5a4103644
1. 3 pillars of ruby.
2. Classes and objects.
3. Inheritance - diving deep
4. Meta programming and reflection - diving deep.
5. Power of method missing.
6. Mixins and ducktyping
7. Super - diving deep
8. Yield
9. Closure
10. Block, proc and lambda
11. More meta programming(examples).
12. Ruby open classes.
2. 3 Pillars of ruby
1. Everything is an object
2. Every operation is a method call on some object.
3. Everything is metaprogramming.
3. 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)
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: 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)
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 international bank 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 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
11. 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
14. allows us to call methods up the inheritance
hierarchy.
15. 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()
16. 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?
17. – 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.
18. Include vs extend
Include: Add module’s methods as instance methods.
Extend: Add module’s methods as class methods.
19. 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)
21. 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)
22. 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
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
28. 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.
30. 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
31. 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." }
36. 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
37. 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}"})
38. 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
39. If lambda are methods 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