# These are examples of new features in Ruby 2.3.0 #--------------------------------------------------------------------------- ## Did You Mean? "Typos suck less now".revers # NoMethodError: undefined method `revers' for "Typos suck less now":String # Did you mean? reverse # reverse! #--------------------------------------------------------------------------- ## The safe navigation operator ## AKA The lonely operator # This could return a User object or nil user = User.first # In Ruby 2.2.x you'd do this to make sure you don't raise an exception if user is nil: if user && user.is_awesome? # ... end # In Ruby 2.3.0 you can use the safe navigation operator instead: if user&.is_awesome? # ... end # It will return nil if the user object is nil # This is super handy when you need to chain these calls together: event_name = api_data&.events&.first&.text&.strip # Think about how succinct this would be when doing any scraping! #--------------------------------------------------------------------------- ## Hash.dig + Array.dig film = { id: 1234, title: "Back to the Future", casts: { cast: [ { name: "Michael J. Fox", character: "Marty McFly" }, { name: "Crispin Glover", character: "George McFly" } ], crew: [ { name: "Robert Zemeckis", role: "Director" } ] } } # To get the first cast member's name, just dig it up film.dig(:casts, :cast, 0, :name) # => "Michael J. Fox" # C'mon. WHAAAAT? #--------------------------------------------------------------------------- ## Numeric.positive? and Numeric.negative? # Useful for filtering Enumerables [1, 12, -2, 100, -99, 44].select(&positive?) # => [1, 12, 100, 44] [1, 12, -2, 100, -99, 44].select(&negative?) # => [-2, 99] #--------------------------------------------------------------------------- ## Enumerable.grep_v # Allows you to find items that don't match you expression in one go star_wars = %w(Vader Yoda Luke Han Leia Chewbacca Jabba) everyone_but_luke = star_wars.grep(/a/) only_luke = star_wars.grep_v(/a/) #--------------------------------------------------------------------------- ## String.freeze # Freezing a string makes it immutable (unchangeable) # This could very well be the default come Ruby 3.0 # The Old Way™ CONSTANT = "Not really a " CONSTANT << " constant" # Not really a constant, totally OK with Ruby! FROZEN = "Can't touch this".freeze FROZEN << " Hammer!" # RuntimeError: can't modify frozen String # The big change here is how it's allocated in memory # Freeze now only allocates one object in memory # 1000 objects for the same string 1000 times! 1000.times.map{ "Heavy duty".object_id }.uniq.size #=> 1000 # Just 1 object! Handy if you refer to strings a lot. 1000.times.map{ "Lightweight".freeze.object_id }.uniq.size #=> 1 #--------------------------------------------------------------------------- # Hash.to_proc car = { make: "Porsche", model: "911", doors: 2, colour: "gunmetal", cylinders: 12, horsepower: 400, price: 120000, origin: "Germany" } features = %i[make model origin] features.map(&car) # => ["Porsche", "911", "Germany"] extended_features = %i[make interior torque] extended_features.map(&car) # => ["Porsche", nil, nil] #--------------------------------------------------------------------------- # Hash.fetch_values # This is a stricter version of Hash.values_at # If the key we're searching for it throws an error car.values_at(:horsepower, :torque) # => [400, nil] car.fetch_values(:horsepower, :torque) #=> KeyError: key not found: :torque #--------------------------------------------------------------------------- # Hash Comparisons (Superset and Subset) one = { title: "Hateful Eight" } some = { title: "Hateful Eight", director: "Quentin Tarantino" } all = { title: "Hateful Eight", director: "Quentin Tarantino", year: 2015 } random = { rockets: true } one < some # => true (one is a strict subset of some) some > some # => false some >= some # => true