Slide 1

Slide 1 text

The Ruby Programming Language Monday, May 6, 13

Slide 2

Slide 2 text

Guido Marucci Blas CTO & Co-Founder at Wolox guidomb [email protected] Monday, May 6, 13

Slide 3

Slide 3 text

The Ruby Programming Language Monday, May 6, 13

Slide 4

Slide 4 text

The Ruby Programming Language 100% Object Oriented Monday, May 6, 13

Slide 5

Slide 5 text

The Ruby Programming Language 100% Object Oriented extremely dynamic Monday, May 6, 13

Slide 6

Slide 6 text

The Ruby Programming Language 100% Object Oriented extremely dynamic developer friendly Monday, May 6, 13

Slide 7

Slide 7 text

The Ruby Programming Language 100% Object Oriented extremely dynamic developer friendly sometimes a little bit magical Monday, May 6, 13

Slide 8

Slide 8 text

100% Object Oriented NO primitive types irb(main):002:0> 1.class => Fixnum irb(main):003:0> 1.methods => [:to_s, :-@, :+, :-, :*, :/, :div, ... ] irb(main):004:0> nil.class => NilClass irb(main):005:0> true.class => TrueClass irb(main):006:0> true.hash => 3677975328212232893 irb(main):007:0> true.methods => [:to_s, :&, :|, :^, :nil?, :===, ...] Monday, May 6, 13

Slide 9

Slide 9 text

Scope: the current object $>irb irb(main):001:0> self => main irb(main):002:0> self.class => Object irb(main):003:0> def foo; puts "hello"; end => nil irb(main):003:0> methods.include?(:foo) => true irb(main):003:0> self.methods.include?(:foo) => nil Monday, May 6, 13

Slide 10

Slide 10 text

Scope: local variables v1 = 1 class MyClass v2 = 2 local_variables # => [:v2] def my_method v3 = 3 local_variables end local_variables # => [:v2] end obj = MyClass.new obj.my_method # => [:v3] Monday, May 6, 13

Slide 11

Slide 11 text

Methods def my_method puts "hello" end def my_method(arg1) puts "hello #{arg1}" end def my_method(*args) args.each {|a| puts a} end my_method(1, 2, 3) Simple method definition Method definition with argument Method definition with variable arguments Monday, May 6, 13

Slide 12

Slide 12 text

Methods def my_method(arg1 = Time.now, options = {}) puts "arg1 #{arg1}" options.each { |k,v| puts "#{k} => #{v}" } end options = {foo: "hello", bar: "bye"} my_method("cool!", options) my_method("cool!", foo: "hello", bar: "bye") my_method("cool!", {foo: "hello", bar: "bye"}) my_method("cool!") my_method Default values & hash arguments Monday, May 6, 13

Slide 13

Slide 13 text

Methods def my_method(arg1, *args) puts "arg1 #{arg1}" options.each { |a| puts "#{a}" } end args = [1, 2, 3] my_method("cool!", *args) my_method("cool!", 1, 2, 3) my_method("cool!") Variable length arguments Monday, May 6, 13

Slide 14

Slide 14 text

Methods def my_method(foo: "hello", bar: "bye", **options) puts "foo #{foo}" puts "bar #{bar}" options.each { |k,v| puts "#{k} => #{v}" } end my_method(foo: "a", bar: 2) my_method(bar: 2, foo: "a") my_method(bar: "a") Keyword arguments Ruby 2.0 Monday, May 6, 13

Slide 15

Slide 15 text

Methods class Fixnum def +(other) super + 1 end end 2 + 2 => 5 Warning!: Do NOT try this at home. There are NO operators, just methods (well almost no operators) Monday, May 6, 13

Slide 16

Slide 16 text

Methods def end_of_the_world? Date.today == Date.parse("2012-12-22") end def notify!(message) puts message SMSNotifier.notify(message) EMailNotifier.notify(message) end def my_property=(property) @property = property end Setter Caution Boolean Monday, May 6, 13

Slide 17

Slide 17 text

Methods def foo puts "calling block" yield if block_given? end foo => calling block foo { puts "inside block" } => calling block => inside block Monday, May 6, 13

Slide 18

Slide 18 text

Methods def bar(&block) block.call foo(&block) end bar { puts "inside block" } => inside block => calling block => inside block def baz(callable) callable.call() foo(&callable) end baz(Proc.new { puts "inside block" }) => inside block => calling block => inside block Monday, May 6, 13

Slide 19

Slide 19 text

Methods a = "hello" a.upcase # => "HELLO" a.send(:upcase) # => "HELLO" a.__send__(:upcase) # => "HELLO" class Foo private def my_secret puts "I love Ruby" end end f = Foo.new f.my_secret # => NoMethodError f.send(:my_secret) # => "I love Ruby" Monday, May 6, 13

Slide 20

Slide 20 text

Methods def foo(num) return if num % 2 == 0 2 + num end a = foo(3) a # => 5 a = foo(2) a # => nil Monday, May 6, 13

Slide 21

Slide 21 text

Blocks are not objects (:-o but you said ... yeah I know) Blocks, Procs & Lambdas Monday, May 6, 13

Slide 22

Slide 22 text

They are like closures. They do not define a new scope. def foo(x) yield x + 1 end my_var = 2 foo(3) {|x| puts x + my_var} # => 6 Blocks, Procs & Lambdas Monday, May 6, 13

Slide 23

Slide 23 text

You can yield them fewer arguments than expected def foo(x) yield x + 1 end foo(3) do |x, y| puts "x = #{x}" puts "y = #{y}" end # => x = 4 # => y = Blocks, Procs & Lambdas Monday, May 6, 13

Slide 24

Slide 24 text

You can yield them more arguments than expected def foo(*args) yield *args end foo(3,4,5,6) do |x| puts "x = #{x}" end # => x = 3 Blocks, Procs & Lambdas Monday, May 6, 13

Slide 25

Slide 25 text

Procs are the object representation of blocks proc = Proc.new { |x,y| x + y } proc.call(1,2) # => 3 proc.(1,2) # => 3 proc[1, 2] # => 3 Blocks, Procs & Lambdas Monday, May 6, 13

Slide 26

Slide 26 text

Blocks, Procs & Lambdas Lambdas are special instances of Proc l = lambda { |x,y| x + y } l.is_a?(Proc) # => true l = ->(x,y) { x + y } l.call(1,2) # => 3 l.(1,2) # => 3 l[1, 2] # => 3 Monday, May 6, 13

Slide 27

Slide 27 text

Blocks, Procs & Lambdas One di erence with procs is that arity MUST be respected. Monday, May 6, 13

Slide 28

Slide 28 text

Blocks, Procs & Lambdas The other di erence is with the return keyword. def double(callable_object) callable_object.call * 2 end l = lambda { return 10 } double(l) # => 20 def another_double p = Proc.new { return 10 } result = p.call return result * 2 # unreachable code end another_double # => 10 Monday, May 6, 13

Slide 29

Slide 29 text

Blocks, Procs & Lambdas def foo yield * 2 end foo { 10 } # => 20 foo { return 10 } # => LocalJumpError class Bar def bar Proc.new { return 10 } end end p = Bar.new.bar foo(&p) #=> LocalJumpError Monday, May 6, 13

Slide 30

Slide 30 text

Strings Interpolation a = "My dynamic string, now is #{Time.now}" b = 'This is not dynamic, now is #{Time.now}' puts a => My dynamic string, now is 2013-04-01 16:57:30 -0300 puts b => This is not dynamic, now is #{Time.now} %q(Interpolationless string 'foo', "bar" & #{Time.now}) => "Interpolationless string 'foo', \"bar\" & \#{Time.now}" %Q(Interpolated string 'foo', "bar" & #{Time.now}) => "Interpolated string 'foo', \"bar\" & 2013-04-01 17:02:02 -0300" Monday, May 6, 13

Slide 31

Slide 31 text

Strings Multiline a = <<-EOS Hello my little world! How are you? EOS => "Hello my little\n world!\n How are\nyou?\n" Monday, May 6, 13

Slide 32

Slide 32 text

Strings are MUTABLE a = "HELLO" a[1] = "A" a # => "HALLO" a.downcase! a #=> "hello" a = "HELLO" b = "HELLO" a == b # => true a.object_id == b.object_id # => false a.equal?(b) # => false Monday, May 6, 13

Slide 33

Slide 33 text

Strings Common pitfall class Foo def initialize(key) @key = key end def test(value) @key == value end end key = "foo" foo = Foo.new(key) foo.test("foo") # => true key.upcase! foo.test("foo") # => false Monday, May 6, 13

Slide 34

Slide 34 text

Strings Some structures save you the trouble key = "foo" map = {} map[key] = 10 map["foo"] # => 10 key.upcase! key # => "FOO" map["foo"] # => 10 map["FOO"] # => nil m_key = map.keys.first m_key == key # => false m_key.frozen? # => true m_key.upcase! # => RuntimeError Monday, May 6, 13

Slide 35

Slide 35 text

Symbols are immutable a = :foo b = :foo c = "foo".to_sym a.equal?(b) # => true b.equal?(c) # => true a.equal?(c) # => true are unique are NOT garbage collected Monday, May 6, 13

Slide 36

Slide 36 text

Symbols random_words = Set.new STDIN.each_with_index do |line, index| words = line.split(/\s+/) break if %w(quit q exit).include?(words.first) random_words.add(words.sample.to_sym) end puts random_words Monday, May 6, 13

Slide 37

Slide 37 text

Symbols random_words = Set.new STDIN.each_with_index do |line, index| words = line.split(/\s+/) break if %w(quit q exit).include?(words.first) random_words.add(words.sample.to_sym) end puts random_words A possible out of memory attack could be done Monday, May 6, 13

Slide 38

Slide 38 text

Equality equal? Tests if two values refers exactly to the same object. == Tests if two values are equals. eql? Alias for equal?. Used as a stricted version of ==. === Case equality. Used in case statement to check if target matches any of the when clauses Monday, May 6, 13

Slide 39

Slide 39 text

Equality a = "foo" b = "foo" a == b # => true a.equal?(b) # => false a === b # => true /^hola(\d)+chau$/ === "hola123chau" => true /^hola(\d)+chau$/ === "holaGATOchau" => false foo = Foo.new Foo === foo => true Foo === 120 => false 1 == 1.0 => true 1.eql?(1.0) => false Monday, May 6, 13

Slide 40

Slide 40 text

Hashes a = Hash.new a = {} a[:foo] = 10 a[:foo] # => 10 hash1 = { "key1" => "foo", "key2" => 120 } hash2 = { key1 => "foo", key2 => 120 } hash3 = { key1: "foo", key2: 120 } Ruby 1.9+ hash1.each do |key, value| puts "#{key} => #{value}" end Monday, May 6, 13

Slide 41

Slide 41 text

Arrays a = Array.new a = [] a[0] = 10 a[0] # => 10 a = [1,4,5,6] a.each do |elem| puts elem end a.map { |elem| elem * 2 } %w(hola chau como estas).map(&:upcase) a.select { |elem| elem % 2 == 0 } a.reduce(0) { |sum, elem| sum + elem } Monday, May 6, 13

Slide 42

Slide 42 text

Lazy enumerators a = [1,3,56,3,5,6,243,212,123,5465,6432,13] a.lazy.map { |n| n * 3 }.select { |n| n % 2 == 0}.each do |n| # Process element n end Ruby 2.0 Monday, May 6, 13

Slide 43

Slide 43 text

Constants PI = 3.14 MyClass = Class.new PI = 3.18 # => warning: already initialized constant PI class Foo BAR = 20 end Foo::BAR # => 20 Foo.const_defined?(:BAR) # => true Foo.const_set(:BAZ, 30) Foo::BAZ # => 30 Monday, May 6, 13

Slide 44

Slide 44 text

Object Oriented Programming Classes Monday, May 6, 13

Slide 45

Slide 45 text

Classes class Person attr_accessor :first_name attr_accessor :last_name def initialize(first_name, last_name) @first_name = first_name @last_name = last_name @birth_date = birth_date end def full_name "#{@first_name} #{@last_name}" end end constructor instance method Monday, May 6, 13

Slide 46

Slide 46 text

Classes class Person attr_accessor :first_name attr_accessor :last_name def initialize(first_name, last_name) @first_name = first_name @last_name = last_name @birth_date = birth_date end def full_name "#{@first_name} #{@last_name}" end end setter & getters constructor instance method Monday, May 6, 13

Slide 47

Slide 47 text

Classes class Person attr_accessor :first_name attr_accessor :last_name def initialize(first_name, last_name) @first_name = first_name @last_name = last_name @birth_date = birth_date end def full_name "#{@first_name} #{@last_name}" end end setter & getters constructor instance method def first_name @first_name end def first_name=(first_name) @first_name = first_name end Monday, May 6, 13

Slide 48

Slide 48 text

Classes class Foo @@class_variable = 3 @class_instance_variable = 4 def initialize @instance_variable = 1 end def foo puts @@class_variable puts @class_instance_variable puts @instance_variable end end Variables Foo.new.foo #=> 3 #=> #=> 1 Monday, May 6, 13

Slide 49

Slide 49 text

Classes class A @@a = 1 @b = 1 def self.foo puts @@a puts @b end end Variables A.foo #=> 2 #=> 1 B.foo #=> 2 #=> 2 class B < A @@a = 2 @b = 2 end Monday, May 6, 13

Slide 50

Slide 50 text

Classes class A def self.class_method puts "Class Method" end def instance_method puts "Instance method" end end Methods class B class << self def class_method_1 puts "Class Method 1" end def class_method_2 puts "Class Method 2" end end end Monday, May 6, 13

Slide 51

Slide 51 text

Classes class A def foo puts "Foo from A" end def bar puts "Bar from A" end end class B < A def foo super puts "Foo from B" end def bar puts "Bar from B" end end a = A.new b = B.new a.foo a.bar # => Foo from A # => Bar from A b.foo b.bar # => Foo from A # => Foo from B # => Bar from B Monday, May 6, 13

Slide 52

Slide 52 text

Classes foo = Foo.new foo.singleton_class class << foo; self; end Singleton Class Eigenclass Metaclass “They are the UFOs of the Ruby world” Monday, May 6, 13

Slide 53

Slide 53 text

Classes MyClass #obj obj Eigenclass Class instance class singleton_class Monday, May 6, 13

Slide 54

Slide 54 text

Classes f1 = Foo.new class << foo def my_method_1 puts "m1" end def my_method_2 puts "m2" end end f1 = Foo.new f2 = Foo.new def f1.foo puts "foo! from f1" end f1.foo # => foo! from f1 f2.foo # => NoMethodError Singleton Methods Monday, May 6, 13

Slide 55

Slide 55 text

Classes MyClass instance_method #obj custom_method obj Eigenclass Class instance class singleton_class #MyClass class_method class MyClass def self.class_method puts 1 end def instance_method puts 2 end end obj = MyClass.new def obj.custom_method puts 3 end singleton_class Monday, May 6, 13

Slide 56

Slide 56 text

Classes BasicObject Kernel Object Module Class #BasicObject #Kernel #Object #Module #Class Eigenclass Class Module Monday, May 6, 13

Slide 57

Slide 57 text

Classes Modules module MyModule def foo puts "Foo from Module" end def bar puts "Bar from Module" end end class MyClass include MyModule include MyOtherModule def foo puts "Foo from #{self}" super end end obj = MyClass.new obj.bar # => Bar from Module obj.foo # => Foo from MyClass # => Foo from OtherModule # => Foo from Module module MyOtherModule def foo puts "Foo from OtherModule" super end end Monday, May 6, 13

Slide 58

Slide 58 text

Classes Modules module MyModule def foo puts "Foo from Module" end def bar puts "Bar from Module" end end class MyClass include MyModule prepend MyOtherModule def foo puts "Foo from #{self}" super end end obj = MyClass.new obj.bar # => Bar from Module obj.foo # => Foo from OtherModule # => Foo from MyClass # => Foo from Module module MyOtherModule def foo puts "Foo from OtherModule" super end end Ruby 2.0 Monday, May 6, 13

Slide 59

Slide 59 text

Classes BasicObject Kernel Object A B #BasicObject #Kernel #Object #A #B #obj obj Eigenclass Class Module instance Monday, May 6, 13

Slide 60

Slide 60 text

Classes Open Classes class String def encrypt chars.map(&:succ).join end end "hello".encrypt # => "ifmmp" Monkey Patching Monday, May 6, 13

Slide 61

Slide 61 text

Classes Refinements module StringExtensions refine String do def encrypt chars.map(&:succ).join end end end "hello".encrypt # => NoMethodError using StringExtensions "hello".encrypt # => "ifmmp" Ruby 2.0 Monday, May 6, 13

Slide 62

Slide 62 text

Ruby Metaprogramming Embrace the dark side of the force Monday, May 6, 13

Slide 63

Slide 63 text

Dynamic Methods class AuditablePerson def first_name logger.info("First name was fetched at #{Time.now}") @first_name end def first_name=(first_name) logger.info("First name was changed from #{@first_name} to #{first_name}") @first_name = first_name end def last_name logger.info("Last name was fetched at #{Time.now}") @last_name end def last_name=(last_name) logger.info("Last name was changed from #{@last_name} to #{last_name}") @last_name = last_name end end Monday, May 6, 13

Slide 64

Slide 64 text

Dynamic Methods module Auditable def self.included(receiver) receiver.extend(ClassMethods) receiver.send(:include, InstanceMethods) end module InstanceMethods end module ClassMethods def attr_auditable(*attributes) attributes.each do |attribute| define_setter(attribute) define_getter(attribute) end end ... end end Monday, May 6, 13

Slide 65

Slide 65 text

Dynamic Methods module Auditable def self.included(receiver) receiver.extend(ClassMethods) receiver.send(:include, InstanceMethods) end module InstanceMethods end module ClassMethods def attr_auditable(*attributes) attributes.each do |attribute| define_setter(attribute) define_getter(attribute) end end ... end end private def define_setter(attribute) var_name = "@#{attribute}".to_sym define_method("#{attribute}=") do |value| old_value = instance_variable_get(var_name) puts("#{attribute} was changed from #{old_value} to #{value} at #{Time.now}") instance_variable_set(var_name, value) end end def define_getter(attribute) var_name = "@#{attribute}".to_sym define_method(attribute) do puts("#{attribute} was fetched at #{Time.now}") instance_variable_get(var_name) end end Monday, May 6, 13

Slide 66

Slide 66 text

Dynamic Methods class Person include Auditable attr_auditable :first_name attr_auditable :last_name end Monday, May 6, 13

Slide 67

Slide 67 text

Method Missing class Recorder def method_missing(method, *args) puts "Method #{method} called with arguments #{args}" end end repository = UserRepository.new(Recorder.new) repository.find_by_name("john") # => Method execute called with arguments SELECT * FROM users WHERE name = "john" Monday, May 6, 13

Slide 68

Slide 68 text

Method Missing module Delegator def self.included(receiver) receiver.extend(ClassMethod) receiver.send(:include, InstanceMethod) end module InstanceMethod def method_missing(method, *args) delegate = send(@@delegate) delegate.send(method, *args) end end module ClassMethod def delegate_to(delegate) @@delegate = delegate end end end Monday, May 6, 13

Slide 69

Slide 69 text

Method Missing class Boss include Delegator attr_reader :secretary delegate_to :secretary def initialize(secretary) @secretary = secretary end end class Secretary def make_finance_report Report.new end end secretary = Secretary.new boss = Boss.new(secretary) report = boss.make_finance_report Monday, May 6, 13

Slide 70

Slide 70 text

Method Missing def respond_to?(method) super || send(@@delegate).respond_to?(method) end Monday, May 6, 13

Slide 71

Slide 71 text

Method Missing Always overwrite respond_to? if you define a method missing def respond_to?(method) super || send(@@delegate).respond_to?(method) end Monday, May 6, 13

Slide 72

Slide 72 text

eval class Foo def initialize @var = 30 end end obj = Foo.new var = obj.instance_eval { @var } # => 30 obj.instance_eval do def var @var end end foo.var # => 30 Foo.new.var # => NoMethodError Foo.class_eval do def var=(var) @var = var end end foo.var = 40 foo.var # => 40 Foo.new.var = 50 Monday, May 6, 13

Slide 73

Slide 73 text

eval class Foo def initialize @var = 30 end end obj = Foo.new var = obj.instance_eval { @var } # => 30 obj.instance_eval do def var @var end end foo.var # => 30 Foo.new.var # => NoMethodError Foo.class_eval do def var=(var) @var = var end end foo.var = 40 foo.var # => 40 Foo.new.var = 50 Monday, May 6, 13

Slide 74

Slide 74 text

class MyClass attr_accessor :attribute_1 attr_accessor :attribute_2 def my_method_1(arg1, arg2) ... end def my_method_2 ... end end obj = Mocker::create(MyClass) obj.attribute_1 # => "foo" obj.attribute_2 # => "bar" obj.attribute_1 = 30 obj.my_method_1(1,2) # => 3 obj.my_method_2 obj.method_called?(:my_method_2) # => true obj.my_method_3 # => NoMethodError mock MyClass do attribute_1 "foo" attribute_2 "bar" when :my_method_1 do |arg1, arg2| arg1 + arg2 end end Monday, May 6, 13

Slide 75

Slide 75 text

Books Monday, May 6, 13

Slide 76

Slide 76 text

This is the end Questions? Monday, May 6, 13