reddot ruby conf 2014 - dark side of ruby
DESCRIPTION
I love Ruby! But as in any relationship, to love means that you (often) have to accept the “dark side” too! Ruby is human in nature and has a lot of gotchas, tricks, weirdness and sometimes scary features that I plan to highlight. This talk aims to provide the “Ah-ha!” moments when working in Ruby. This talk is for beginners and experts alike - in fact, I tag slides to mark their level and beginners can choose to tune out of the heavy stuff! My talk shall cover the dark side of the following features of Ruby (in no particular order) Keyword wierdness method missing Module inheritance! (huh?) Accessor righteousness Curried Procs for the hungry Base Conversions Cherry picking module methods Oniguruma games Object id weirdness procs, blocks and our friend stabby. ==, ===, eql? and equal? and more… As with most of my talks, humor plays an important role and I shall aim to get everyone high on Ruby with a deep dive!TRANSCRIPT
The Dark Side of Ruby
@gautamrege!@joshsoftware
What’s the talk about?
What’s the talk about?• Ruby is AWESOME but …
What’s the talk about?• Ruby is AWESOME but …
• Nothing scary
What’s the talk about?• Ruby is AWESOME but …
• Nothing scary really
What’s the talk about?• Ruby is AWESOME but …
• Nothing scary
• Weirdness and Gotcha’s
really
What’s the talk about?• Ruby is AWESOME but …
• Nothing scary
• Weirdness and Gotcha’s
Ah-ha! Moments
really
Slides are Tagged
Slides are Tagged
Beginner
Slides are Tagged
Expert
(In)Famous Infinityhttp://www.flickr.com/photos/emdot/482622478/sizes/l/
(In)Famous Infinity
(In)Famous Infinity
(In)Famous Infinity$ irb> 1/0
(In)Famous Infinity$ irb> 1/0=> ZeroDivisionError: divided by 0
(In)Famous Infinity$ irb> 1/0
$ irb> 1.0/0
=> ZeroDivisionError: divided by 0
(In)Famous Infinity$ irb> 1/0
$ irb> 1.0/0
=> ZeroDivisionError: divided by 0
=> Infinity
(In)Famous Infinity$ irb> 1/0
$ irb> 1.0/0
$ irb> Infinity
=> ZeroDivisionError: divided by 0
=> Infinity
(In)Famous Infinity$ irb> 1/0
$ irb> 1.0/0
$ irb> Infinity
=> ZeroDivisionError: divided by 0
=> Infinity
=> NameError: uninitialized constant Infinity
(In)Famous Infinity
(In)Famous Infinity
(In)Famous Infinity#if !defined(INFINITY) || !defined(NAN)!union bytesequence4_or_float {! unsigned char bytesequence[4];! float float_value;!};!#endif!!RUBY_EXTERN const union bytesequence4_or_float rb_infinity;
(In)Famous Infinity#if !defined(INFINITY) || !defined(NAN)!union bytesequence4_or_float {! unsigned char bytesequence[4];! float float_value;!};!#endif!!RUBY_EXTERN const union bytesequence4_or_float rb_infinity;
include/ruby/missing.h
Base Jumping
http://www.flickr.com/photos/shahdi/8035647153/sizes/l/
Base Conversions
Base Conversions
Base Conversions$ irb> 12345.to_s(8)
Base Conversions$ irb> 12345.to_s(8)=> "30071" # => Octal
Base Conversions$ irb> 12345.to_s(8)
$ irb> 12345.to_s(36)
=> "30071" # => Octal
Base Conversions$ irb> 12345.to_s(8)
$ irb> 12345.to_s(36)
=> "30071" # => Octal
=> "9ix" # That is an actual number
Base Conversions$ irb> 12345.to_s(8)
$ irb> 12345.to_s(36)
$ irb> 1234.to_s(64)
=> "30071" # => Octal
=> "9ix" # That is an actual number
Base Conversions$ irb> 12345.to_s(8)
$ irb> 12345.to_s(36)
$ irb> 1234.to_s(64)
=> "30071" # => Octal
=> "9ix" # That is an actual number
=> ArgumentError: invalid radix 64
Hashes and Arrays
Hashes and Arrays
Hashes and Arraysa=[1,2,3,4,5,6]!h=Hash[*a]
Hashes and Arraysa=[1,2,3,4,5,6]!h=Hash[*a]=> {1=>2, 3=>4, 5=>6}
Hashes and Arraysa=[1,2,3,4,5,6]!h=Hash[*a]=> {1=>2, 3=>4, 5=>6}
[1,2,3] * 3
Hashes and Arraysa=[1,2,3,4,5,6]!h=Hash[*a]=> {1=>2, 3=>4, 5=>6}
[1,2,3] * 3=> [1,2,3,1,2,3,1,2,3]
Hashes and Arraysa=[1,2,3,4,5,6]!h=Hash[*a]=> {1=>2, 3=>4, 5=>6}
[1,2,3] * 3=> [1,2,3,1,2,3,1,2,3]
[1,2,3] * "%"
Hashes and Arraysa=[1,2,3,4,5,6]!h=Hash[*a]=> {1=>2, 3=>4, 5=>6}
[1,2,3] * 3=> [1,2,3,1,2,3,1,2,3]
[1,2,3] * "%"=> "1%2%3"
Calling out to Stabby
Calling out to Stabbyblk = ->(f, *m, sl, l) do puts sl end
Calling out to Stabbyblk = ->(f, *m, sl, l) do puts sl end
blk.call(1, 2, 3, 4, 5, 6)
Calling out to Stabbyblk = ->(f, *m, sl, l) do puts sl end
blk.call(1, 2, 3, 4, 5, 6)=> 5
blk.(1, 2, 3, 4, 5, 6)
Calling out to Stabbyblk = ->(f, *m, sl, l) do puts sl end
blk.call(1, 2, 3, 4, 5, 6)=> 5
blk.(1, 2, 3, 4, 5, 6)
Calling out to Stabbyblk = ->(f, *m, sl, l) do puts sl end
blk.call(1, 2, 3, 4, 5, 6)=> 5
blk.(1, 2, 3, 4, 5, 6)=> 5
syntax sugar for call blk[1,2,3,4,5,6]
Calling out to Stabbyblk = ->(f, *m, sl, l) do puts sl end
blk.call(1, 2, 3, 4, 5, 6)=> 5
Syntax
Syntax def foo(a=1, b=1,opts={})
end
Syntax def foo(a: 1, b: 2)
end
Syntax def foo(a: 1, b: 2)
endkeyword arguments
Syntax
def foo(a: 1, b:, c: 2)
end
def foo(a: 1, b: 2)
endkeyword arguments
Syntax
def foo(a: 1, b:, c: 2)
end
def foo(a: 1, b: 2)
endkeyword arguments
Syntax
def foo(a: 1, b:, c: 2)
end
foo(a: 2) => ArgumentError: missing keyword: b
Mandatory keyword arguments
def foo(a: 1, b: 2)
endkeyword arguments
Syntax
Syntaxdef foo(a, b) p a, b
end
Syntaxdef foo(a, b) p a, b
end
foo (1, 2)
Syntaxdef foo(a, b) p a, b
end
foo (1, 2)syntax error, unexpected
',', expecting ')'
Syntaxdef foo(a, b) p a, b
end
foo (1, 2)
foo(1, 2)
Syntaxdef foo(a, b) p a, b
end
foo (1, 2)
foo(1, 2)
Syntaxdef foo(a, b) p a, b
end
foo (1, 2)
foo(1, 2) syntax error, unexpected ',', expecting ')'
Syntax
Syntaxirb> a[1]
What is the data type of a ?
Syntaxirb> a[1]
a = [ 10, 11, 12] # Array
Syntaxirb> a[1]
a = [ 10, 11, 12] # Array
a = { 1 => "one" } # Hash
Syntaxirb> a[1]
a = [ 10, 11, 12] # Array
a = { 1 => "one" } # Hash
a = Proc.new { |e| p e }
Case Complexity
The Case Statementdef multiple_of(factor)! Proc.new {|p| p.modulo(factor).zero?}!end!!
number = 9!case number! when multiple_of(3)! puts "Multiple of 3"! when multiple_of(7)! puts "Multiple of 7"!end
The Case Statementdef multiple_of(factor)! Proc.new {|p| p.modulo(factor).zero?}!end!!
number = 9!case number! when multiple_of(3)! puts "Multiple of 3"! when multiple_of(7)! puts "Multiple of 7"!end
Behind every case is a ===number = 9!case number ! when multiple_of(3)
Proc.new {|p| p.modulo(3).zero?} === 9
Behind every case is a ===number = 9!case number ! when multiple_of(3)
Proc.new {|p| p.modulo(3).zero?} === 9
Behind every case is a ===number = 9!case number ! when multiple_of(3)
Proc.new {|p| p.modulo(3).zero?} === 9
Proc#=== is an alias to Proc#call.
Behind every case is a ===number = 9!case number ! when multiple_of(3)
Proc.new {|p| p.modulo(3).zero?} === 9
Proc.new { |p| ! p.modulo(3).zero?!}.call(9)
Proc#=== is an alias to Proc#call.
Override the === method to customise case
evaluation.
==, ===, eql?, equal?
http://www.flickr.com/photos/gak/2418146934/sizes/o/
==, ===, eql?, equal?
==, ===, eql?, equal?irb> 1 == 1.0
==, ===, eql?, equal?irb> 1 == 1.0=> true # generic equality
==, ===, eql?, equal?irb> 1 == 1.0=> true # generic equalityirb> 1 === 1.0
==, ===, eql?, equal?irb> 1 == 1.0=> true # generic equalityirb> 1 === 1.0=> true # case equality
==, ===, eql?, equal?irb> 1 == 1.0=> true # generic equalityirb> 1 === 1.0=> true # case equalityirb> 1.eql? 1.0
==, ===, eql?, equal?
=> false # alias of == except Numeric
irb> 1 == 1.0=> true # generic equalityirb> 1 === 1.0=> true # case equalityirb> 1.eql? 1.0
==, ===, eql?, equal?
=> false # alias of == except Numeric
irb> 1 == 1.0=> true # generic equalityirb> 1 === 1.0=> true # case equalityirb> 1.eql? 1.0
irb> 1.equal? 1.0
==, ===, eql?, equal?
=> false # alias of == except Numeric
irb> 1 == 1.0=> true # generic equalityirb> 1 === 1.0=> true # case equalityirb> 1.eql? 1.0
irb> 1.equal? 1.0=> false # object identity
==, ===, eql?, equal?
=> false # alias of == except Numeric
irb> 1 == 1.0=> true # generic equalityirb> 1 === 1.0=> true # case equalityirb> 1.eql? 1.0
irb> 1.equal? 1.0=> false # object identityirb> 'a'.equal? 'a'
==, ===, eql?, equal?
=> false # alias of == except Numeric
=> false # gotcha?
irb> 1 == 1.0=> true # generic equalityirb> 1 === 1.0=> true # case equalityirb> 1.eql? 1.0
irb> 1.equal? 1.0=> false # object identityirb> 'a'.equal? 'a'
==, ===, eql?, equal?
=> false # alias of == except Numeric
=> false # gotcha?
irb> 1 == 1.0=> true # generic equalityirb> 1 === 1.0=> true # case equalityirb> 1.eql? 1.0
irb> 1.equal? 1.0=> false # object identityirb> 'a'.equal? 'a'
irb> 1.equal? 1
==, ===, eql?, equal?
=> false # alias of == except Numeric
=> false # gotcha?
irb> 1 == 1.0=> true # generic equalityirb> 1 === 1.0=> true # case equalityirb> 1.eql? 1.0
irb> 1.equal? 1.0=> false # object identityirb> 'a'.equal? 'a'
=> true # gotcha?irb> 1.equal? 1
Object Ids & Fixnum
Object Ids & Fixnum
Fixnum * 2 + 1irb> 23 * 2 + 1 => 47 irb > 23.object_id => 47
Object Ids & Fixnum
Fixnum * 2 + 1irb> 23 * 2 + 1 => 47 irb > 23.object_id => 47
irb> (2**62 - 1).class => Fixnum irb> (2**62).class => Bignum
http://www.flickr.com/photos/miscdebris/6748016253/sizes/o/
3 Pulls for the Jackpotjackpot = lambda { |x, y, z| (x == y) == (x == z) } # 3 pulls pull = jackpot.curry[rand(5)] 2.times { pull = pull.curry[rand(5)] } !
p pull ? "Jackpot" : "Sucker!"
3 Pulls for the Jackpotjackpot = lambda { |x, y, z| (x == y) == (x == z) } # 3 pulls pull = jackpot.curry[rand(5)] 2.times { pull = pull.curry[rand(5)] } !
p pull ? "Jackpot" : "Sucker!"
3 Pulls for the Jackpotjackpot = lambda { |x, y, z| (x == y) == (x == z) } # 3 pulls pull = jackpot.curry[rand(5)] 2.times { pull = pull.curry[rand(5)] } !
p pull ? "Jackpot" : "Sucker!"
The curry recipe
• Return lambda till all parameters are passed.
• Evaluate the block if all parameters are passed.
pull = jackpot.curry[rand(5)] => #<Proc:0x007f9eec0990b0 (lambda)>
2.times { pull = pull.curry[rand(5)] } => true # or false
The curry recipe
• Return lambda till all parameters are passed.
• Evaluate the block if all parameters are passed.
pull = jackpot.curry[rand(5)] => #<Proc:0x007f9eec0990b0 (lambda)>
2.times { pull = pull.curry[rand(5)] } => true # or false
So! So you think you can tell…
Heaven from Hell- Pink Floyd
So! So you think you can tell…
Protected from Private
Private methodsclass Soldier private def ryan puts "Saving private ryan" end end class Movie < Soldier def name ryan end end
Private methodsclass Soldier private def ryan puts "Saving private ryan" end end class Movie < Soldier def name ryan end end
Private methodsclass Soldier private def ryan puts "Saving private ryan" end end class Movie < Soldier def name ryan end end
Private Methods are inherited!
class Base! include Mongoid::Document!end
The elusive include
class Base! include Mongoid::Document!end
The elusive include
Private method!Instance method !
Defined the class Module
class Base! include Mongoid::Document!end
The elusive include
Protected methods
Protected methods
Protected methods
• Work with objects not classes.
Protected methods
• Work with objects not classes.
• Invoke a protected method on another object in the same lineage
Protected methods
• Work with objects not classes.
• Invoke a protected method on another object in the same lineage
What the …
class Autobot def initialize(nick); @nick = nick; end !
protected attr_accessor :nick end !prime = Autobot.new("Optimus Prime") p prime.nick
class Autobot def initialize(nick); @nick = nick; end !
protected attr_accessor :nick end !prime = Autobot.new("Optimus Prime") p prime.nick
class Autobot def initialize(nick); @nick = nick; end !
protected attr_accessor :nick end !prime = Autobot.new("Optimus Prime") p prime.nick
protected method `nick' called for #<Autobot:0x007f92ba082330 @nick="Optimus Prime"> (NoMethodError)
class Autobot def fights(target) p "I am #{self.nick}" p "Kicking #{target.nick}'s ass" end protected attr_accessor :nick end !optimus = Autobot.new("Optimus Prime") megatron = Autobot.new('Megatron') !optimus.fights megatron
class Autobot def fights(target) p "I am #{self.nick}" p "Kicking #{target.nick}'s ass" end protected attr_accessor :nick end !optimus = Autobot.new("Optimus Prime") megatron = Autobot.new('Megatron') !optimus.fights megatron
class Autobot def fights(target) p "I am #{self.nick}" p "Kicking #{target.nick}'s ass" end protected attr_accessor :nick end !optimus = Autobot.new("Optimus Prime") megatron = Autobot.new('Megatron') !optimus.fights megatron
"I am Optimus Prime" "Kicking Megatron's ass"
class Autobot def fights(target) p "I am #{self.nick}" p "Kicking #{target.nick}'s ass" end protected attr_accessor :nick end !optimus = Autobot.new("Optimus Prime") megatron = Autobot.new('Megatron') !optimus.fights megatron
"I am Optimus Prime" "Kicking Megatron's ass"
Keywords in Ruby?
Keywords - hmm…class Serious def true false end def false true end end die = Serious.new p "seriously!" if die.false
Keywords - hmm…class Serious def true false end def false true end end die = Serious.new p "seriously!" if die.false
Keywords - hmm…class Serious def true false end def false true end end die = Serious.new p "seriously!" if die.false
stack too deep?
superlativeclass Search! def search! p "Default Search algorithm"! end!end!class KeywordSearch < Search ! def search(keyword:)! super! end!end!KeywordSearch.new.search(keyword: "Ruby")
superlativeclass Search! def search! p "Default Search algorithm"! end!end!class KeywordSearch < Search ! def search(keyword:)! super! end!end!KeywordSearch.new.search(keyword: "Ruby")
superlativeclass Search! def search! p "Default Search algorithm"! end!end!class KeywordSearch < Search ! def search(keyword:)! super! end!end!KeywordSearch.new.search(keyword: "Ruby")
superlativeclass Search! def search! p "Default Search algorithm"! end!end!class KeywordSearch < Search ! def search(keyword:)! super! end!end!KeywordSearch.new.search(keyword: "Ruby")
`search`: wrong number of arguments (1 for 0)
superlativeclass Search! def search! p "Default Search algorithm"! end!end!class KeywordSearch < Search ! def search(keyword:)! super()! end!end!KeywordSearch.new.search(keyword: "Ruby")
superlativeclass Search! def search! p "Default Search algorithm"! end!end!class KeywordSearch < Search ! def search(keyword:)! super()! end!end!KeywordSearch.new.search(keyword: "Ruby")
superlativeclass Search! def search! p "Default Search algorithm"! end!end!class KeywordSearch < Search ! def search(keyword:)! super()! end!end!KeywordSearch.new.search(keyword: "Ruby")
superlativeclass Search! def search! p "Default Search algorithm"! end!end!class KeywordSearch < Search ! def search(keyword:)! super()! end!end!KeywordSearch.new.search(keyword: "Ruby")
And we thought parenthesis for method invocation didn’t matter
Module mixins are funnymodule Superman! def fly; p "Superman: It's a bird"; end!end!!module Batman! def fly; p "Batman: Fly? Me?"; end!end!!module Ironman! def fly; p "Iroman: That's flying!"; end!end
Module mixins are funnymodule Superman! def fly; p "Superman: It's a bird"; end!end!!module Batman! def fly; p "Batman: Fly? Me?"; end!end!!module Ironman! def fly; p "Iroman: That's flying!"; end!end
Module mixins are funnyclass Tinyman! include Superman! include Batman! include Ironman!end!!Tinyman.new.fly
"Iroman: That's how you fly!"
Module mixins are funnyclass Tinyman! include Superman! include Batman! include Ironman!end!!Tinyman.new.fly
"Iroman: That's how you fly!"
Module Inheritance?module Superman! def fly; p "Superman: It's a bird"; end!end!!module Batman! def fly; p "Batman: Fly? Me?"; end!end!!module Ironman! def fly; super; p "Iroman: That's flying!"; end!end
Module Inheritance?module Superman! def fly; p "Superman: It's a bird"; end!end!!module Batman! def fly; p "Batman: Fly? Me?"; end!end!!module Ironman! def fly; super; p "Iroman: That's flying!"; end!end
Module Inheritance?module Superman! def fly; p "Superman: It's a bird"; end!end!!module Batman! def fly; p "Batman: Fly? Me?"; end!end!!module Ironman! def fly; super; p "Iroman: That's flying!"; end!end
"Batman: Fly? Me?”!"Iroman: That's flying!"
Dynamic Inheritance!class Tinyman! include Superman! include Batman! include Ironman!end
Dynamic Inheritance!class Tinyman! include Superman! include Batman! include Ironman!end
class Tinyman! include Superman! include Ironman! include Batman!end
Dynamic Inheritance!class Tinyman! include Superman! include Batman! include Ironman!end
class Tinyman! include Superman! include Ironman! include Batman!end
Dynamic Inheritance!class Tinyman! include Superman! include Batman! include Ironman!end
Cherry pick from Modules
module Megatron! def power! p "Megatron's super strength"! end!!
def evil! p 'Evil genius'! end!end
Cherry pick from Modules
module Megatron! def power! p "Megatron's super strength"! end!!
def evil! p 'Evil genius'! end!end
class Hanuman! include Megatron!end
Hanuman.new.power!# => "Megatron's super strength"!Hanuman.new.evil !# => "Evil genius" # Oh no!
Cherry pick from Modules
class Hanuman! include Megatron!end
Hanuman.new.power!# => "Megatron's super strength"!Hanuman.new.evil !# => "Evil genius" # Oh no!
Cherry pick from Modules
Cherry pick from Modulesclass Hanuman! def power! Megatron.instance_method(:power).! bind(self).call! end!end
Cherry pick from Modulesclass Hanuman! def power! Megatron.instance_method(:power).! bind(self).call! end!end
Cherry pick from Modulesclass Hanuman! def power! Megatron.instance_method(:power).! bind(self).call! end!end
Hanuman.new.power# => "Megatron's super strength"Hanuman.new.evil# => undefined method `evil’...>
That’s all Folks!
@gautamrege @joshsoftware
That’s all Folks!
@gautamrege @joshsoftware
since 2007