reddot ruby conf 2014 - dark side of ruby

Post on 10-May-2015

2.529 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

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

top related