RedDot Ruby Conf 2014 - Dark side of ruby

9,392 views

Published on

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!

Published in: Software, Technology, Education

RedDot Ruby Conf 2014 - Dark side of ruby

  1. The Dark Side of Ruby @gautamrege! @joshsoftware
  2. What’s the talk about?
  3. What’s the talk about? • Ruby is AWESOME but …
  4. What’s the talk about? • Ruby is AWESOME but … • Nothing scary
  5. What’s the talk about? • Ruby is AWESOME but … • Nothing scary really
  6. What’s the talk about? • Ruby is AWESOME but … • Nothing scary • Weirdness and Gotcha’s really
  7. What’s the talk about? • Ruby is AWESOME but … • Nothing scary • Weirdness and Gotcha’s Ah-ha! Moments really
  8. Slides are Tagged
  9. Slides are Tagged Beginner
  10. Slides are Tagged Expert
  11. (In)Famous Infinityhttp://www.flickr.com/photos/emdot/482622478/sizes/l/
  12. (In)Famous Infinity
  13. (In)Famous Infinity
  14. (In)Famous Infinity $ irb> 1/0
  15. (In)Famous Infinity $ irb> 1/0 => ZeroDivisionError: divided by 0
  16. (In)Famous Infinity $ irb> 1/0 $ irb> 1.0/0 => ZeroDivisionError: divided by 0
  17. (In)Famous Infinity $ irb> 1/0 $ irb> 1.0/0 => ZeroDivisionError: divided by 0 => Infinity
  18. (In)Famous Infinity $ irb> 1/0 $ irb> 1.0/0 $ irb> Infinity => ZeroDivisionError: divided by 0 => Infinity
  19. (In)Famous Infinity $ irb> 1/0 $ irb> 1.0/0 $ irb> Infinity => ZeroDivisionError: divided by 0 => Infinity => NameError: uninitialized constant Infinity
  20. (In)Famous Infinity
  21. (In)Famous Infinity
  22. (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;
  23. (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
  24. Base Jumping http://www.flickr.com/photos/shahdi/8035647153/sizes/l/
  25. Base Conversions
  26. Base Conversions
  27. Base Conversions $ irb> 12345.to_s(8)
  28. Base Conversions $ irb> 12345.to_s(8) => "30071" # => Octal
  29. Base Conversions $ irb> 12345.to_s(8) $ irb> 12345.to_s(36) => "30071" # => Octal
  30. Base Conversions $ irb> 12345.to_s(8) $ irb> 12345.to_s(36) => "30071" # => Octal => "9ix" # That is an actual number
  31. 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
  32. 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
  33. Hashes and Arrays
  34. Hashes and Arrays
  35. Hashes and Arrays a=[1,2,3,4,5,6]! h=Hash[*a]
  36. Hashes and Arrays a=[1,2,3,4,5,6]! h=Hash[*a] => {1=>2, 3=>4, 5=>6}
  37. Hashes and Arrays a=[1,2,3,4,5,6]! h=Hash[*a] => {1=>2, 3=>4, 5=>6} [1,2,3] * 3
  38. Hashes and Arrays a=[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]
  39. Hashes and Arrays a=[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] * "%"
  40. Hashes and Arrays a=[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"
  41. Calling out to Stabby
  42. Calling out to Stabby blk = ->(f, *m, sl, l) do puts sl end
  43. Calling out to Stabby blk = ->(f, *m, sl, l) do puts sl end blk.call(1, 2, 3, 4, 5, 6)
  44. Calling out to Stabby blk = ->(f, *m, sl, l) do puts sl end blk.call(1, 2, 3, 4, 5, 6) => 5
  45. blk.(1, 2, 3, 4, 5, 6) Calling out to Stabby blk = ->(f, *m, sl, l) do puts sl end blk.call(1, 2, 3, 4, 5, 6) => 5
  46. blk.(1, 2, 3, 4, 5, 6) Calling out to Stabby blk = ->(f, *m, sl, l) do puts sl end blk.call(1, 2, 3, 4, 5, 6) => 5
  47. blk.(1, 2, 3, 4, 5, 6) => 5 syntax sugar for call blk[1,2,3,4,5,6] Calling out to Stabby blk = ->(f, *m, sl, l) do puts sl end blk.call(1, 2, 3, 4, 5, 6) => 5
  48. Syntax
  49. Syntax def foo(a=1, b=1,opts={}) end
  50. Syntax def foo(a: 1, b: 2) end
  51. Syntax def foo(a: 1, b: 2) end keyword arguments
  52. Syntax def foo(a: 1, b:, c: 2) end def foo(a: 1, b: 2) end keyword arguments
  53. Syntax def foo(a: 1, b:, c: 2) end def foo(a: 1, b: 2) end keyword arguments
  54. 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) end keyword arguments
  55. Syntax
  56. Syntax def foo(a, b) p a, b end
  57. Syntax def foo(a, b) p a, b end foo (1, 2)
  58. Syntax def foo(a, b) p a, b end foo (1, 2) syntax error, unexpected ',', expecting ')'
  59. Syntax def foo(a, b) p a, b end foo (1, 2) foo(1, 2)
  60. Syntax def foo(a, b) p a, b end foo (1, 2) foo(1, 2)
  61. Syntax def foo(a, b) p a, b end foo (1, 2) foo(1, 2) syntax error, unexpected ',', expecting ')'
  62. Syntax
  63. Syntax irb> a[1] What is the data type of a ?
  64. Syntax irb> a[1] a = [ 10, 11, 12] # Array
  65. Syntax irb> a[1] a = [ 10, 11, 12] # Array a = { 1 => "one" } # Hash
  66. Syntax irb> a[1] a = [ 10, 11, 12] # Array a = { 1 => "one" } # Hash a = Proc.new { |e| p e }
  67. Case Complexity
  68. The Case Statement def 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
  69. The Case Statement def 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
  70. Behind every case is a === number = 9! case number ! when multiple_of(3) Proc.new {|p| p.modulo(3).zero?} === 9
  71. Behind every case is a === number = 9! case number ! when multiple_of(3) Proc.new {|p| p.modulo(3).zero?} === 9
  72. 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.
  73. 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.
  74. Override the === method to customise case evaluation.
  75. ==, ===, eql?, equal? http://www.flickr.com/photos/gak/2418146934/sizes/o/
  76. ==, ===, eql?, equal?
  77. ==, ===, eql?, equal? irb> 1 == 1.0
  78. ==, ===, eql?, equal? irb> 1 == 1.0 => true # generic equality
  79. ==, ===, eql?, equal? irb> 1 == 1.0 => true # generic equality irb> 1 === 1.0
  80. ==, ===, eql?, equal? irb> 1 == 1.0 => true # generic equality irb> 1 === 1.0 => true # case equality
  81. ==, ===, eql?, equal? irb> 1 == 1.0 => true # generic equality irb> 1 === 1.0 => true # case equality irb> 1.eql? 1.0
  82. ==, ===, eql?, equal? => false # alias of == except Numeric irb> 1 == 1.0 => true # generic equality irb> 1 === 1.0 => true # case equality irb> 1.eql? 1.0
  83. ==, ===, eql?, equal? => false # alias of == except Numeric irb> 1 == 1.0 => true # generic equality irb> 1 === 1.0 => true # case equality irb> 1.eql? 1.0 irb> 1.equal? 1.0
  84. ==, ===, eql?, equal? => false # alias of == except Numeric irb> 1 == 1.0 => true # generic equality irb> 1 === 1.0 => true # case equality irb> 1.eql? 1.0 irb> 1.equal? 1.0 => false # object identity
  85. ==, ===, eql?, equal? => false # alias of == except Numeric irb> 1 == 1.0 => true # generic equality irb> 1 === 1.0 => true # case equality irb> 1.eql? 1.0 irb> 1.equal? 1.0 => false # object identity irb> 'a'.equal? 'a'
  86. ==, ===, eql?, equal? => false # alias of == except Numeric => false # gotcha? irb> 1 == 1.0 => true # generic equality irb> 1 === 1.0 => true # case equality irb> 1.eql? 1.0 irb> 1.equal? 1.0 => false # object identity irb> 'a'.equal? 'a'
  87. ==, ===, eql?, equal? => false # alias of == except Numeric => false # gotcha? irb> 1 == 1.0 => true # generic equality irb> 1 === 1.0 => true # case equality irb> 1.eql? 1.0 irb> 1.equal? 1.0 => false # object identity irb> 'a'.equal? 'a' irb> 1.equal? 1
  88. ==, ===, eql?, equal? => false # alias of == except Numeric => false # gotcha? irb> 1 == 1.0 => true # generic equality irb> 1 === 1.0 => true # case equality irb> 1.eql? 1.0 irb> 1.equal? 1.0 => false # object identity irb> 'a'.equal? 'a' => true # gotcha? irb> 1.equal? 1
  89. Object Ids & Fixnum
  90. Object Ids & Fixnum Fixnum * 2 + 1 irb> 23 * 2 + 1 => 47 irb > 23.object_id => 47
  91. Object Ids & Fixnum Fixnum * 2 + 1 irb> 23 * 2 + 1 => 47 irb > 23.object_id => 47 irb> (2**62 - 1).class => Fixnum irb> (2**62).class => Bignum
  92. http://www.flickr.com/photos/miscdebris/6748016253/sizes/o/
  93. 3 Pulls for the Jackpot jackpot = 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!"
  94. 3 Pulls for the Jackpot jackpot = 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!"
  95. 3 Pulls for the Jackpot jackpot = 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!"
  96. 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
  97. 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
  98. So! So you think you can tell… Heaven from Hell - Pink Floyd
  99. So! So you think you can tell… Protected from Private
  100. Private methods class Soldier private def ryan puts "Saving private ryan" end end class Movie < Soldier def name ryan end end
  101. Private methods class Soldier private def ryan puts "Saving private ryan" end end class Movie < Soldier def name ryan end end
  102. Private methods class Soldier private def ryan puts "Saving private ryan" end end class Movie < Soldier def name ryan end end Private Methods are inherited!
  103. class Base! include Mongoid::Document! end The elusive include
  104. class Base! include Mongoid::Document! end The elusive include
  105. Private method! Instance method ! Defined the class Module class Base! include Mongoid::Document! end The elusive include
  106. Protected methods
  107. Protected methods
  108. Protected methods • Work with objects not classes.
  109. Protected methods • Work with objects not classes. • Invoke a protected method on another object in the same lineage
  110. Protected methods • Work with objects not classes. • Invoke a protected method on another object in the same lineage What the …
  111. class Autobot def initialize(nick); @nick = nick; end ! protected attr_accessor :nick end ! prime = Autobot.new("Optimus Prime") p prime.nick
  112. class Autobot def initialize(nick); @nick = nick; end ! protected attr_accessor :nick end ! prime = Autobot.new("Optimus Prime") p prime.nick
  113. 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)
  114. 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
  115. 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
  116. 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"
  117. 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"
  118. Keywords in Ruby?
  119. Keywords - hmm… class Serious def true false end def false true end end die = Serious.new p "seriously!" if die.false
  120. Keywords - hmm… class Serious def true false end def false true end end die = Serious.new p "seriously!" if die.false
  121. Keywords - hmm… class Serious def true false end def false true end end die = Serious.new p "seriously!" if die.false stack too deep?
  122. superlative class Search! def search! p "Default Search algorithm"! end! end! class KeywordSearch < Search ! def search(keyword:)! super! end! end! KeywordSearch.new.search(keyword: "Ruby")
  123. superlative class Search! def search! p "Default Search algorithm"! end! end! class KeywordSearch < Search ! def search(keyword:)! super! end! end! KeywordSearch.new.search(keyword: "Ruby")
  124. superlative class Search! def search! p "Default Search algorithm"! end! end! class KeywordSearch < Search ! def search(keyword:)! super! end! end! KeywordSearch.new.search(keyword: "Ruby")
  125. superlative class 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)
  126. superlative class Search! def search! p "Default Search algorithm"! end! end! class KeywordSearch < Search ! def search(keyword:)! super()! end! end! KeywordSearch.new.search(keyword: "Ruby")
  127. superlative class Search! def search! p "Default Search algorithm"! end! end! class KeywordSearch < Search ! def search(keyword:)! super()! end! end! KeywordSearch.new.search(keyword: "Ruby")
  128. superlative class Search! def search! p "Default Search algorithm"! end! end! class KeywordSearch < Search ! def search(keyword:)! super()! end! end! KeywordSearch.new.search(keyword: "Ruby")
  129. superlative class 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
  130. Module mixins are funny 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; p "Iroman: That's flying!"; end! end
  131. Module mixins are funny 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; p "Iroman: That's flying!"; end! end
  132. Module mixins are funny class Tinyman! include Superman! include Batman! include Ironman! end! ! Tinyman.new.fly "Iroman: That's how you fly!"
  133. Module mixins are funny class Tinyman! include Superman! include Batman! include Ironman! end! ! Tinyman.new.fly "Iroman: That's how you fly!"
  134. 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
  135. 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
  136. 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!"
  137. Dynamic Inheritance! class Tinyman! include Superman! include Batman! include Ironman! end
  138. Dynamic Inheritance! class Tinyman! include Superman! include Batman! include Ironman! end
  139. class Tinyman! include Superman! include Ironman! include Batman! end Dynamic Inheritance! class Tinyman! include Superman! include Batman! include Ironman! end
  140. class Tinyman! include Superman! include Ironman! include Batman! end Dynamic Inheritance! class Tinyman! include Superman! include Batman! include Ironman! end
  141. Cherry pick from Modules module Megatron! def power! p "Megatron's super strength"! end! ! def evil! p 'Evil genius'! end! end
  142. Cherry pick from Modules module Megatron! def power! p "Megatron's super strength"! end! ! def evil! p 'Evil genius'! end! end
  143. class Hanuman! include Megatron! end Hanuman.new.power! # => "Megatron's super strength"! Hanuman.new.evil ! # => "Evil genius" # Oh no! Cherry pick from Modules
  144. class Hanuman! include Megatron! end Hanuman.new.power! # => "Megatron's super strength"! Hanuman.new.evil ! # => "Evil genius" # Oh no! Cherry pick from Modules
  145. Cherry pick from Modules class Hanuman! def power! Megatron.instance_method(:power).! bind(self).call! end! end
  146. Cherry pick from Modules class Hanuman! def power! Megatron.instance_method(:power).! bind(self).call! end! end
  147. Cherry pick from Modules class 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’...>
  148. That’s all Folks! @gautamrege @joshsoftware
  149. That’s all Folks! @gautamrege @joshsoftware since 2007

×