I was at Bath Ruby this week!

I’ve never managed to motivate myself to take decent notes at a conference, so this time I tried something different — I livetweeted the whole thing.

I also made last-minute decision to put my name down a lightning talk, and was fortunate enough to get the last slot. The talk was “My Favourite Parts of the Ruby Standard Library that You Might Not Know About”, and it was really well received. People were especially impressed with the way I handled some computer issues while getting set up, where I couldn’t get my computer to respond for a good couple of minutes after standing up at the speakers’ podium.

I used my new presenting setup for the second time here, which makes me even more confident this is how I’ll do it going forward: rather than trying to arrange attractive slides using Keynote or similar, I just created a load of text buffers in my editor, and cycled through them in order. This let me create my slides in an environment I was much more comfortable with and familiar in, in a plain text format that I can post directly on the web and reüse in future, and without graphical features that get me distracted trying to make everything pixel-perfect.

Here were the examples I gave (not necessarily in order):

Delegate

Create wrapper objects that can delegate to the object they contain. This is especially useful for implementing the decorator pattern, which I’ve done here with a decorator that wraps and object and logs all messages sent to it by other objects.

require "delegate"

class Greeter
  def greet(name)
    "Hello name"
  end
end

class LogMessages < SimpleDelegator
  def method_missing(name, *args)
    result = super
    Kernel.puts "#{__getobj__.inspect}.#{name}(#{args.map(&:inspect).join(", ")}) -> #{result.inspect}"
    result
  end
end

greeter = LogMessages.new(Greeter.new)
greeter.greet("BathRuby")
# -> #<Greeter:0x00007fadc98e6448>.greet("BathRuby") -> "Hello name"

Forwardable

Ruby has a built-in method delegation macro very similar to the much more commonly-known implementation provided by Rails. We can use it like this:

require "forwardable"

class User
  extend Forwardable

  # delegate the `name` message to profile
  def_delegator :profile, :name

  def profile
    Profile.new(name: "Alyssa")
  end
end

class Profile
  attr_reader :name

  def initialize(name:)
    @name = name
  end
end

puts User.new.name
# -> Alyssa

OptionParser

Ruby has a built-in library for parsing command line options, generating help text, etc., as would otherwise often be handled by an external Gem such as Thor:

#!/usr/bin/env ruby
require "optparse"

options = {}

OptionParser.new do |parser|
  parser.banner = "Usage: #{File.basename(__FILE__)} [options]"
  parser.on "--name NAME", String, "name of the person to be greeted" do |name|
    options[:name] = name
  end
end.parse!

puts "Hello #{options.fetch(:name)}"

This creates a script that can be used like this:

$ ./greet.rb --help
Usage: greet.rb [options]
        --name NAME                  name of the person to be greeted

$ ./greet.rb --name BathRuby
Hello BathRuby

Note that OptionParser automatically intercepts --help, prints automatically formatted help text, then exits the program.

REXML

This is probably the one that least people knew about coming into my talk. Ruby has a built-in XML library, so you don’t necessarily need to include a big library like Nokogiri with a large native extension:

require "rexml/document"

xml = <<XML
<?xml version="1.0" encoding="UTF-8"?>
<conferences>
  <conference name="BathRuby" />
  <conference name="Brighton Ruby" />
</conferences>
XML

document = REXML::Document.new(xml)
document.root.get_elements("//conference")
# => [<conference name='BathRuby'/>, <conference name='Brighton Ruby'/>]

The downside to REXML is that it’s implemented in pure Ruby, so Nokogiri or another external XML library may well be worth including if adding a dependency is worth the performance gain of using a C implementation.

Shellwords

Shellwords lets you escape strings so that they can be interpolated into shell commands. In most cases, this is unnecessary, since you can (and should) pass dynamic values into shell commands in Ruby using an arguments array (system "say", name) rather than a single command string (system "say #{name}"). However, shellwords still comes in handy when you want to pass a whole shell command as a single argument (for example to be executed in a subshell). For example:

require "shellwords"

print "Enter name: "
name = gets.chomp

command = "echo Hello #{name.shellescape}; read"
system "tmux", "split-window", "bash", "-c", command

Singleton

The singleton pattern is often discouraged nowadays, but if you do find yourself in a position to be implementing it, the Ruby standard library has your back:

require "singleton"

class Twitter
  include Singleton

  def tweet(text)
    puts "Tweeting #{text}"
  end
end

Twitter.new.tweet("hello")
# ~> private method `new' called for Twitter:Class

Twitter.instance.tweet("hello")
# -> Tweeting hello

Conclusion

I put this talk together over a conference lunch, and only had five minutes on stage, so there’s loads more cool stuff in the Ruby standard library I could have gone into but didn’t. I highly encourage you to explore it for yourself. Some more of my favourites are Fiddle and StringScanner.