Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Coding Confidently With Ruby

Coding Confidently With Ruby

A talk for Ruby Conf PH 2015

Maricris Nonato

March 27, 2015
Tweet

More Decks by Maricris Nonato

Other Decks in Programming

Transcript

  1. “If we have data, let’s look at data. If all

    we have are opinions, let’s go with mine” Jim Barksdale, former Netscape CEO
  2. Method Target Class Type Notes #to_a Array Explicit #to_ary Array

    Implicit #to_c Complex Explicit #to_h Hash Explicit Introduced in Ruby 2.0 #to_i Integer Explicit #to_int Integer Implicit #to_path String Implicit #to_r Rational Explicit #to_regexp Regexp Implicit Used by Regexp.try_convert #to_s String Explicit #to_str String Implicit #to_sym Symbol Implicit
  3. class Cat def initialize(breed, color) @breed = breed @color =

    color end end cat = Cat.new(breed: 'Munchkin cat', color: 'ginger') DATA INPUTS
  4. class Cat def initialize(breed, color) @breed = breed @color =

    color end end cat = Cat.new(breed: 'Munchkin cat', color: 'ginger') DATA INPUTS puts "I'm looking for a " + cat # => TypeError: can't convert Cat into String # Implicit
  5. class Cat def initialize(breed, color) @breed = breed @color =

    color end end cat = Cat.new(breed: 'Munchkin cat', color: 'ginger') DATA INPUTS puts "I'm looking for a “ + cat.to_s # => I have a #<Cat:0x007f81a40ce1d0> # Explicit puts "I'm looking for a " + cat # => TypeError: can't convert Cat into String # Implicit
  6. class Cat def initialize(breed, color) @breed = breed @color =

    color end end cat = Cat.new(breed: 'Munchkin cat', color: 'ginger') DATA INPUTS puts "I'm looking for a “ + cat.to_s # => I have a #<Cat:0x007f81a40ce1d0> # Explicit puts "I'm looking for a " + cat # => TypeError: can't convert Cat into String # Implicit # String Interpolation puts "I'm looking for a #{cat}” # => I have a #<Cat:0x007f81a40ce1d0>
  7. class Duck def quack 'Quack!' end end DATA INPUTS class

    DuckRecording def quack play end def play 'Quack!' end end
  8. class Duck def quack 'Quack!' end end DATA INPUTS class

    DuckRecording def quack play end def play 'Quack!' end end def make_it_quack(duck) duck.quack end # => “Quack!” puts make_it_quack(Duck.new) # => “Quack!” puts make_it_quack(DuckRecording.new)
  9. class Duck def quack 'Quack!' end end class Cow def

    moo 'Moooo' end end DATA INPUTS
  10. class Duck def quack 'Quack!' end end class Cow def

    moo 'Moooo' end end class DuckRecording def quack play end def play 'Quack!' end end DATA INPUTS
  11. class Duck def quack 'Quack!' end end class Cow def

    moo 'Moooo' end end class DuckRecording def quack play end def play 'Quack!' end end DATA INPUTS def make_it_quack(animal) animal.moo if animal.is_a?(Cow) end def make_it_quack(animal) raise TypeError unless animal.is_a?(Duck) end
  12. class Duck def quack 'Quack!' end end class Cow def

    moo 'Moooo' end end class DuckRecording def quack play end def play 'Quack!' end end DATA INPUTS def make_it_quack(animal) animal.moo if animal.is_a?(Cow) end def make_it_quack(animal) raise TypeError unless animal.is_a?(Duck) end
  13. class Cow def soundoff moo end end class Duck def

    soundoff quack end end DATA INPUTS def make_it_quack(animal) animal.quack if animal.respond_to?(quack) end #Solution #1: def make_it_quack(animal) animal.soundoff end #Solution #2:
  14. Integer(10) # => 10 Integer(10.1) # => 10 Integer(“0x10") #

    => 16 Integer(0x10) # => 16 Integer(“010") # => 8 Integer(“0b10") # => 2 Integer(Time.now) # => 1427065879 Integer("cats”) # ArgumentError DATA INPUTS
  15. Integer(10) # => 10 Integer(10.1) # => 10 Integer(“0x10") #

    => 16 Integer(0x10) # => 16 Integer(“010") # => 8 Integer(“0b10") # => 2 Integer(Time.now) # => 1427065879 Integer("cats”) # ArgumentError DATA INPUTS (10).to_int # => 10 (10.1).to_int # => 10 (“0x10”).to_int # NoMethodError (0x10).to_int # => 16 (“010”).to_int # NoMethodError (“0b10”).to_int # NoMethodError (Time.now).to_int # NoMethodError (“cats”).to_int # NoMethodError
  16. Integer(10) # => 10 Integer(10.1) # => 10 Integer(“0x10") #

    => 16 Integer(0x10) # => 16 Integer(“010") # => 8 Integer(“0b10") # => 2 Integer(Time.now) # => 1427065879 Integer("cats”) # ArgumentError DATA INPUTS (10).to_int # => 10 (10.1).to_int # => 10 (“0x10”).to_int # NoMethodError (0x10).to_int # => 16 (“010”).to_int # NoMethodError (“0b10”).to_int # NoMethodError (Time.now).to_int # NoMethodError (“cats”).to_int # NoMethodError (10).to_i # => 10 (10.1).to_i # => 10 (“0x10”).to_i # => 0 (0x10).to_i # => 16 (“010”).to_i # => 10 (“0b10”).to_i # => 0 (Time.now).to_i # 1427065879 (“cats”).to_i # => 0
  17. def point_label(point) point.to_latlng if point.respond_to?(:to_latlng) end DATA INPUTS class MapPoint

    def initialize(x, y) @x, @y = x, y end def to_latlng “location at: #{@x}, #{@y}” end end Conversion method for convenience
  18. def point_label(point) point.to_latlng if point.respond_to?(:to_latlng) end DATA INPUTS class MapPoint

    def initialize(x, y) @x, @y = x, y end def to_latlng “location at: #{@x}, #{@y}” end end Conversion method for convenience point_label(MapPoint.new(11.9694, 121.9272)) # => ”location at: 11.9694, 121.9272"
  19. def money_on_wallet(peso, bitcoin) peso + bitcoin end DATA INPUTS Conversion

    function for strict requirements def money_on_wallet(peso, bitcoin) peso.to_f + (bitcoin.to_f * 1.5) end def money_on_wallet(peso, bitcoin) Float(peso) + (Float(bitcoin) * 1.5) end
  20. def money_on_wallet(peso, bitcoin) Peso(peso) + bitcoin.to_peso end DATA INPUTS Conversion

    function for strict requirements class Bitcoin def initialize(value) @value = value end def to_peso Peso(@value) * 1.5 end end def Peso(input) raise TypeError if input.is_a?(String) Float(input) end
  21. def age if birthdate.present? # do something else 0 end

    end DATA INPUTS class Person def initialize(name, birthdate) @name = name @birthdate = birthdate end end
  22. def minor? begin if birthdate + 18.years < Date.today #

    do something end rescue # error when birthdate is nil end end def age if birthdate.present? # do something else 0 end end DATA INPUTS class Person def initialize(name, birthdate) @name = name @birthdate = birthdate end end
  23. def minor? begin if birthdate + 18.years < Date.today #

    do something end rescue # error when birthdate is nil end end def age if birthdate.present? # do something else 0 end end DATA INPUTS class Person def initialize(name, birthdate) @name = name @birthdate = birthdate end end
  24. require 'date' class Person def initialize(name, birthdate) @name = name

    self.birthdate = birthdate end def birthdate=(new_birthdate) raise TypeError, "Invalid birth date" unless new_birthdate.is_a?(Date) @birthdate = new_birthdate end end DATA INPUTS
  25. DATA INPUTS person = Person.new(“Marilyn”, “01/01/1958”) TypeError: Invalid birth date

    from (irb):114:in `birthdate=' from (irb):110:in `initialize' from (irb):134:in `new' from (irb):134 from /bin/irb:11:in `<main>' person = Person.new(“Enzo”, Date.new(2011,03,26)) => #<Person:0x007f980901b190 @name="Enzo", @birthdate=#<Date: 2011-03-26 ((2455647j,0s,0n), +0s,2299161j)>>
  26. require 'date' class Person def initialize(name, birthdate) @name = name

    self.birthdate = birthdate end def birthdate=(new_birthdate) raise TypeError, "Invalid birth date" unless new_birthdate.is_a? (Date) @birthdate = new_birthdate end def age now = Date.today now.year - @birthdate.year - ((now.month > @birthdate.month || (now.month == @birthdate.month && now.day >= @birthdate.day)) ? 0 : 1) end def minor? true if age < 18 end end DATA INPUTS
  27. h = { foo: false, bar: nil } DATA INPUTS

    # test for key 'foobar' h.has_key?(:foobar) && h[:foobar].present?
  28. h = { foo: false, bar: nil } DATA INPUTS

    # test for key 'foobar' h.has_key?(:foobar) && h[:foobar].present? # ambiguity demonstrated
  29. h = { foo: false, bar: nil } DATA INPUTS

    # test for key 'foobar' h.has_key?(:foobar) && h[:foobar].present? # ambiguity demonstrated h[:foobar].inspect # => “nil” h[:foo].inspect # => “false” h[:bar].inspect # => “nil” h.fetch(:foobar) # => "KeyError: key not found: :foobar" h.fetch(:foo) # => false h.fetch(:bar) # => nil
  30. h = { foo: false, bar: nil } DATA INPUTS

    # test for key 'foobar' h.has_key?(:foobar) && h[:foobar].present? # ambiguity demonstrated h[:foobar].inspect # => “nil” h[:foo].inspect # => “false” h[:bar].inspect # => “nil” h.fetch(:foobar) # => "KeyError: key not found: :foobar" h.fetch(:foo) # => false h.fetch(:bar) # => nil # error can be customised as well! raise KeyError, "Ooops, that key is missing!”
  31. DATA INPUTS • fetch(key_name): get the value if the key

    exists, raise a KeyError if it doesn’t • fetch(key_name, default_value): get the value if the key exists, return default_value otherwise • fetch(key_name) { |key| "default" }: get the value if the key exists, otherwise run the supplied block and return the value.
  32. DATA INPUTS h = { foo: false, bar: nil }

    h.fetch(:foobar) KeyError: key not found: :foobar from (irb):159:in `fetch' from (irb):159 from /bin/irb:11:in `<main>'
  33. DATA INPUTS h = { foo: false, bar: nil }

    h.fetch(:foobar, “Hello world!” => "Hello World!" h.fetch(:foobar) KeyError: key not found: :foobar from (irb):159:in `fetch' from (irb):159 from /bin/irb:11:in `<main>'
  34. DATA INPUTS h = { foo: false, bar: nil }

    h.fetch(:foobar, “Hello world!” => "Hello World!" h.fetch(:foobar) { |key| “Hello again World!” } => "Hello again World!" h.fetch(:foobar) KeyError: key not found: :foobar from (irb):159:in `fetch' from (irb):159 from /bin/irb:11:in `<main>'
  35. def current_user if session[:user_id] User.find(session[:user_id]) end end def greeting "Hello,

    " + current_user.name : "Anonymous" end if current_user render_logout_button else render_login_button end DATA INPUTS
  36. class GuestUser def initialize(session) @session = session end def name

    "Anonymous" end def authenticated? false end end DATA INPUTS
  37. class GuestUser def initialize(session) @session = session end def name

    "Anonymous" end def authenticated? false end end def current_user if session[:user_id] User.find(session[:user_id]) else GuestUser.new(session) end end DATA INPUTS
  38. class GuestUser def initialize(session) @session = session end def name

    "Anonymous" end def authenticated? false end end def current_user if session[:user_id] User.find(session[:user_id]) else GuestUser.new(session) end end DATA INPUTS def greeting "Hello, #{current_user.name}" end if current_user.authenticated? render_logout_button else render_login_button end
  39. def render_details(person) content_tag(:h1, person.name) + \ content_tag(:h2, person.nickname) end DATA

    INPUTS class Person def nickname nick end end class Person def nickname nick || first_name end end def render_details(person) content_tag(:h1, person.name) + \ content_tag(:h2, person.nickname) end
  40. DATA INPUTS class Line def initialize(pointX, pointY) end end class

    Triangle def initialize(pointX, pointY, pointZ) end end point = Struct.new(:x, :y)
  41. DATA INPUTS class Line def initialize(pointX, pointY) end end class

    Triangle def initialize(pointX, pointY, pointZ) end end point = Struct.new(:x, :y) class Plot def draw_line(Line.new(pointX, pointY)) end def draw_triangle(Triangle.new(pointX, pointY, pointZ)) end end
  42. def search(hash_params) results = SearchModel.search(hash_params) if results.nil? return nil else

    case results.size when 0 0 when 1 results.first.to_json else results.collect{|x| x.to_json} end end end RETURNING DATA
  43. GET /stores/{store_hash}/v2/blog/posts [ { "id": 2, "title": "Ten Great New

    Products", "url": "/blog/ten-great-new-products/", "body": "<p>Here's ten new products that make great gifts...</ p>", "summary": "Here's ten new products that make great gifts...", "author": "Clary Johnson" }, { "id": 3, "title": "The Laughing Monsters: A Novel", "url": "/blog/laughing-monsters-a-novel/", "body": "<p>Denis Johnson's The Laughing Monsters is a high- suspense tale</p>", "summary": "Denis Johnson's The Laughing Monsters is a high- suspense tale", "author": "Denis Johnson" }, ] RETURNING DATA
  44. def create_profile(attributes) unless @banned # do something else otherwise end

    end def create_profile(attributes) return if @banned # do something else otherwise end RETURNING DATA
  45. floor = [["blank", "blank", "blank"], ["gummy", "blank", "blank"], ["blank", "blank",

    "blank"]] attempts = 0 candy = nil catch(:found) do floor.each do |row| row.each do |tile| attempts += 1 if tile == "jawbreaker" || tile == "gummy" candy = tile throw(:found) end end end end ERROR HANDLING https://rubymonk.com/learning/books/4-ruby-primer-ascent/ chapters/41-exceptions/lessons/93-throw-and-catch
  46. floor = [["blank", "blank", "blank"], ["gummy", "blank", "blank"], ["blank", "blank",

    "blank"]] attempts = 0 candy = nil catch(:found) do floor.each do |row| row.each do |tile| attempts += 1 if tile == "jawbreaker" || tile == "gummy" candy = tile throw(:found) end end end end ERROR HANDLING puts candy # => gummy puts attempts # => 4 https://rubymonk.com/learning/books/4-ruby-primer-ascent/ chapters/41-exceptions/lessons/93-throw-and-catch
  47. floor = [["blank", "blank", "blank"], ["gummy", "blank", "blank"], ["blank", "blank",

    "blank"]] attempts = 0 candy = catch(:found) do floor.each do |row| row.each do |tile| attempts += 1 throw(:found, tile) if tile == "jawbreaker" || tile == "gummy" end end end ERROR HANDLING https://rubymonk.com/learning/books/4-ruby-primer-ascent/ chapters/41-exceptions/lessons/93-throw-and-catch
  48. floor = [["blank", "blank", "blank"], ["gummy", "blank", "blank"], ["blank", "blank",

    "blank"]] attempts = 0 candy = catch(:found) do floor.each do |row| row.each do |tile| attempts += 1 throw(:found, tile) if tile == "jawbreaker" || tile == "gummy" end end end ERROR HANDLING puts candy # => gummy puts attempts # => 4 https://rubymonk.com/learning/books/4-ruby-primer-ascent/ chapters/41-exceptions/lessons/93-throw-and-catch
  49. begin # - rescue OneTypeOfException # - rescue AnotherTypeOfException #

    - else # Other more general exceptions ensure # Always will be executed end ERROR HANDLING
  50. def your_buggy_method do_work1_method do_work_2_method end def do_work_1_method do_work1 rescue Exception

    e do_rescue_1 end def do_work_2_method do_work2 rescue Exception e do_rescue_2 end Solution #1: ERROR HANDLING
  51. def your_buggy_method do_work1_method do_work_2_method end def do_work_1_method do_work1 rescue Exception

    e do_rescue_1 end def do_work_2_method do_work2 rescue Exception e do_rescue_2 end Solution #1: ERROR HANDLING Solution #2:
  52. def your_buggy_method do_work1_method do_work_2_method end def do_work_1_method do_work1 rescue Exception

    e do_rescue_1 end def do_work_2_method do_work2 rescue Exception e do_rescue_2 end Solution #1: ERROR HANDLING Solution #2: def your_buggy_method do_work1 rescue Exception e do_rescue_1 do_work2 rescue Exception e do_rescue_2 end
  53. • Confident Ruby: 32 Patterns for Joyful Coding by Avdi

    Grimm • Exceptional Ruby: Master the Art of Handling Failure in Ruby by Avdi Grimm • Eloquent Ruby by Russ Olsen • Effective Ruby: 48 Specific Ways to Write Better Ruby by Peter J. Jones Further Readings