guh.me - gustavo's personal blog

Book review – Ruby Best Practices

Ruby is a nice and expressive language, and full of hidden tricks. It takes time for new programmers to learn them, and Ruby Best Practices speeds this process. Expert programmers can also benefit from the book by learning new tricks and remembering forgotten ones. As a seasoned Ruby newbie, I’ve found the book to be mostly useful.

The book is basically a collection of strategies to solve common problems in Ruby, and it’s full of real world examples. The author, Gregory T. Brown, is an experienced Rubyist who authored libraries like Prawn, a PDF documents generator (it’s very good!). Given his practical experience, the book is filled with real and relevant examples and intelligent and elegant solutions to them.

Besides the quality content, the book is well written, straightforward and funny. Gregory is enthusiastic and he keeps you hooked wanting to read more and more, which is something very interesting in a technical book. And even though the book was released in 2009, the problems and code are still valid.

This book is a great read for the ones who aspire becoming great (or at least good) Rubyists. ;)

Book on Amazon: Ruby Best Practices

My personal notes

Chapter 1: driving code through tests

Chapter 2: designing beautiful APIs

class Server
  # other methods same as before
  def self.run(port=3333,&block)
    server = Server.new(port)
    server.instance_eval(&block)
    server.run
  end
end

Server.run do
  handle(/hello/i) { "Hello from server at #{Time.now}" }
  handle(/goodbye/i) { "Goodbye from server at #{Time.now}" }
  handle(/name is (\w+)/) { |m| "Nice to meet you #{m[1]}!" }
end
!!(obj)
!!(:foo)
!!(123)

Chapter 4: text and file processing

(5..10).inject(:+)
(5..10).inject('+')
(5..10).inject { |sum, n| sum + n }

Chapter 5: functional programming

Appendix

Code examples

# logger.rb
require 'logger'

class StandardError
  def report
    %{#{self.class}: #{message}\n#{backtrace.join("\n")}}
  end
end

logger = Logger.new('newb.log')
logger.error 'Foo barrrrrrr!'
logger.info 'Fus rooooh!'
logger.fatal 'Dead!'
logger.warn 'Danger!'
logger.debug 'Here comes the error'
# meta.rb
class MetaProg
  def self.go(&block)
    m = MetaProg.new
    m.instance_eval &block
    m.say
  end
  
  def yell(sentence)
    puts sentence
  end
  
  def say
    puts 'say foo!'
  end
end

MetaProg.go do |m|
  m.yell 'Hayoooo!'
  m.yell 'Silver!'
end
# module.rb
module Foo
  extend self
  
  def bar
    puts 'baz'
  end
  
  module Baz
    extend self
    
    def biz
      puts 'bir'
    end
  end
end

Foo.bar
Foo::Baz.biz
# Rakefile

task default: [:bang]

desc 'Faz algo inútil'
task :bang do
  sh 'pwd'
  puts `ruby -v`
end

desc 'Imprime o texto shoop da whoop no terminal'
task :shoop do
  puts "sh00p da wh00p"
end

namespace :bar do
  desc 'Does bar:foo without name clash.'
  task :foo do
    puts 'task bar:foo'
  end
end
# stubs.rb
module Stubber
  extend self
  
  def stubs(method, options = {})
    singleton(options[:for]).send(:define_method, method) do |*a|
      options[:returns]
    end
  end
  
  def singleton(obj)
    class << obj
      self
    end
  end
end

class User
end

user = User.new
puts user.inspect
Stubber.stubs(:logged_in?, for: user, returns: true)

# True
puts user.logged_in?

# NoMethodError
puts User.new.logged_in?