Ruby 2.5: How’s That Backtrace looking?

Posted by on December 21, 2017

It’s Christmas night, the air is cool and the stars are unseen through the heavy cloud. Children have left tablet and a nip of whisky out for Santa, and carrots out for the reindeer. One of the children wakes up! Rushing out of bed, she heads to the living room, curious to see if she can catch Santa and reindeer in the act of present delivery.

When she gets there, she finds the food and drink have gone. However there are footprints and reindeer tracks, which lead outside.

As developers, we’re fairly used to following a trail of clues from a point of interest. When a program you’re developing crashes or raises a controlled exception, your tools will typically provide you with as much information as it can about the error. In Ruby, this typically includes a stack trace, showing all the call points on the way to the error. Ruby calls this a “backtrace”.

def turtle_doves
   partridge_in_pear_trees = 1
   partridges(partridge_in_pear_trees)
end

def partridges(number)
  raise “Shouldn’t this be recursive?”
end

turtle_doves

$ ruby test.rb
test.rb:7:in `partridges': Shouldn't this be recursive? (RuntimeError)
    from test.rb:3:in `turtle_doves'
    from test.rb:10:in `<main>'

Something you may not have thought about much is the order in which this stack trace is presented to you. As you can see above, in Ruby 2.4 and earlier you’ll see the error, followed by the location of the code that caused the error, and then site that called that location and so on back to the first line of the program. On a command line, this means that you’ll often see the least useful thing closest to your cursor.

For small stack traces, this is fairly handy! It puts the error information right next to the line that caused the error. For longer stack traces, that you’d typically see in game programming or Rack based web applications, it means you need to scroll off screen to find the error site.

Four years ago, someone suggested a Ruby feature to allow the order of this stack trace to be reversed. This feature will land in Ruby 2.5!

This is backtrace we get from running the same function above under 2.5:

$ ruby test.rb
Traceback (most recent call last):
  2: from test.rb:10:in `<main>'
  1: from test.rb:3:in `turtle_doves'
test.rb:7:in `partridges': Shouldn't this be recursive? (RuntimeError)

This is really handy for those longer stack traces, and is actually how other languages (such as Python) have handled stack traces for a while. However, if you have tooling that parses error output like this (such as a test tool), it will likely be broken by this change. The core team decided to make this a non-optional change for this reason; it means that tooling like this doesn’t have to work out which ordering is being used.

I’m really looking forward to being able to use this; I suspect it will make debugging much easier.

See you tomorrow for one more post about a Ruby 2.5 change!

Ruby 2.5: Not Blocking My Rescue

Posted by on December 20, 2017

Rescuing specific exceptions excessively can cause problems, but if you’ve ever had need to rescue within a do/end block, you might have found yourself using wordy syntax. Ruby 2.5 has a solution for you.

In Ruby 2.5, we’ll get a little syntactic sugar for handling exceptions inside do/end blocks. You can see the feature discussion on Ruby’s Redmine instance. If you’ve ever used the shorthand for rescuing inside a method without using begin/end keywords, this is basically that but inside blocks.

Below we’re going to work through a bit of code, but we’re not going to define all the methods.

Imagine a Santa class, and an algorithm for Santa Claus arriving in town. Santa needs some paper to jot down delivery details. If there is no paper, we raise an error.

class Santa
  def initialize(good, bad)
    @nice_children = good
    @naughty_children = bad
  end

  def make_list
    raise NoPaperError if @paper.nil?
    @list = nice_children.gifts.map(&:details)
  end

  def check_list
    @list.verify_children(nice_children, naughty_children)
  end

   # other Santastic behaviour below
end

We’ll define a global prepare method that takes a block. In Ruby 2.4 when we send the make_list message to Santa within the block, we need to use a full begin/rescue/end clause to describe the behaviour:

def song(children)
  santa = Santa.new(children.good, children.judged_capriciously_by_society)
  prepare do
    # must use `begin` here in Ruby 2.4 and earlier
    begin
      santa.make_list
      2.times { santa.check_list }
    rescue NoPaperError
       santa.request_paper
    ensure
       music_stops
    end
  end
end

def prepare(&block)
  yield
end

If we try to use the shorthand common in Classes, we hit an exception:

def song(children)
  santa = Santa.new(children.good, children.judged_capriciously_by_society)
  prepare do
    santa.make_list
    2.times { santa.check_list }

    rescue NoPaperError
      santa.request_paper
    ensure
      music_stops
    end
  end
end

song(children) #=> SyntaxError:  syntax error, unexpected keyword_rescue, expecting keyword_end

In Ruby 2.5 though, we can do this handily:

def song(children)
  santa = Santa.new(children.good, children.judged_capriciously_by_society)
  prepare do
    # no `begin` keyword!
    santa.make_list
    2.times { santa.check_list }

    rescue NoPaperError
      santa.request_paper
    ensure
      music_stops
    end
  end
end

song(children) # => “music stops”

So that’s nice. See you tomorrow for more Ruby 2.5 news!

Ruby 2.5: yield_self

Posted by on December 19, 2017

yield_self is coming to Ruby 2.5. What is this long requested feature, and how does it work?

Some features take a while to get into a Ruby release. As you can see from the original request on Ruby’s Redmine issue tracker, yield_self has been brewing for 5 years. It has been waiting on a good name, and the Ruby team has settled on one.

But what is it? To understand it, perhaps it is best to look at some existing features.
Ruby blocks are one of the ways of passing around functions in Ruby. Every method parameter list has an implicit block argument, which can be used within the method. One of the ways of activating a block is to call yield. For example:

class GiftGiver
  ...
  def deliver(gift, &additional_behaviour)
    raise NoGiftError if @gift_tracker[gift.name] == 0
    @gift_tracker[gift.name] -= 1
    yield
  end
end

gifter = GiftGiver.new(gift)

# Santa Claus
gifter.deliver(new_ruby_version) do |gift|
  puts “Ho ho ho, a holly Merry Christmas. Here’s your #{gift}”
end

# Ruby core team
gifter.deliver(new_ruby_version) do |gift|
  puts “メリークリスマス!”
end

The tap method was introduced in Ruby 1.9, and allows you to inspect on and act on a chain of methods. You can see how it’s used in the documentation.

(1..10)                .tap {|x| puts "original: #{x.inspect}"}
  .to_a                .tap {|x| puts "array: #{x.inspect}"}
  .select {|x| x%2==0} .tap {|x| puts "evens: #{x.inspect}"}
  .map {|x| x*x}       .tap {|x| puts "squares: #{x.inspect}"}
“original: 1..10”
“array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
“evens: [2, 4, 6, 8, 10]
“squares:  [1,4,9,16,25,36,49,64,81,100]”
=> [1,4,9,16,25,36,49,64,81,100]

The advantage of tap is that it always returns the value of the method tap was called on. This means in the above chain, apart from the puts calls, the result is the same as doing:

(1..10)
  .to_a
  .select {|x| x%2==0}
  .map {|x| x*x}
=> [1,4,9,16,25,36,49,64,81,100]

So, where does yield_self come in? It works similarly to tap, but it returns the value of the block you execute each time.

4.tap { |num| num*num } #=> 4
4.yield_self { |num| num*num } #=> 16

This lets you chain together methods that ordinarily wouldn’t be chainable. An example of this is using class methods:

filename.yield_self {|filename| File.open(filename)}.
               yield_self {|file| file.read}.
               yield_self {|contents| YAML.load(contents)}.
               yield_self {|yaml| yaml.fetch[“spec_directory”]}

To do this naively without yield_self, you can nest arguments:

YAML.load(File.open(filename).read).fetch[“spec_directory”]

So, the trade off here is between a sequence and nesting arguments. There may be some cases where having some sequence methods allows you to be either more expressive or more comprehensible.

You can see a good discussion of this over on Michał Łomnicki’s blog. We’re interested to see where this goes and how frequently it is used!

Ruby 2.5: The Christmas Present

Posted by on December 18, 2017

Let’s talk a wee bit about Ruby 2.5. This starts a week of looking at upcoming features in the language.

Here at FreeAgent Towers, we use a lot of Ruby. The application itself is written in Rails, and our website is static HTML, CSS and JS, but is generated with Middleman. We love it, and so we follow its development closely. One of the exciting points in the Ruby year is the annual release of the next new version of CRuby (formerly MRI). This release has traditionally been around Christmas, and this year is scheduled for Christmas day!

We thought you might be interested in learning about what’s coming down the line, so you know what to look for when you pick up the new version. So over the next few days, we’ll put out a series of small posts, each highlighting one feature from Ruby 2.5.

To get you started, did you know that Ruby 2.5-head is currently around 10% faster than Ruby 2.4.1, and so 165% faster than Ruby 2.0? These are benchmarks so you’ll see different results in production, but those are some impressive improvements.

Until tomorrow!

Many timezones, one team – how do you stand up?

Posted by on January 6, 2016

We like to keep our product teams small. A mixture of designers, engineers and product people working together to add new features and make improvements to different areas of the app. To help keep each team together, we operate with a morning stand up each day, which is designed to help keep everyone up to date. I’m not going to try to sell you on stand ups – if you haven’t heard of them, or are having difficulty in finding them useful, Jason Yip’s classic paper is a great place to start. Instead, I’d like to write about operating stand ups across multiple timezones.

Be Upstanding

In the last year, my team at FreeAgent has changed to include members in the UK, France and Canada. Clearly we can’t all be at a single morning meeting, since our mornings start 6 hours apart. We kept to our morning stand up time which allowed our French colleague to take part over Skype, an hour after the start of work. This left Harry, the Torontonian, asleep and let the rest of us start our days.

At the end of the UK work day, there were still 5 hours of work for Harry to do, so when he finished his day, he effectively started standing down. He posted the things he’d done in the team chatroom, requested help with any obstacles he had encountered that day and made requests for code reviews.

Closing the loop

This was great for the rest of us, and allowed us to help Harry out during our mornings. We even started talking about his obstacles during our standup. But how was he to know? It is always important that your team feels cohesive, so I started taking notes in our stand ups, and then posting them in our shared chatroom for Harry to read.

We found two immediate benefits:

  1. easy to see what was said by whom, and it gave Harry the same experience we had from his stand down.
  2. for the note taker a much deeper engagement in the process than usual.

These came with two downsides though:

  1. One person writing them up spends a bit of their morning (15 minutes) writing up the stand up (which takes place in a room with a computer for Skype as per usual).
  2. It still isn’t an entirely equal experience.

You can combat the first downside by spreading the note taking around the team. This also means everyone gets to share in the benefit of deeper engagement.

As for the second downside? Well, we like experimenting to make our processes better, so we’re trying something else for this. Stay tuned for the next episode in a few weeks time.

Do you run remote stand ups across multiple timezones? We’d love to hear about your experiences and suggestions!