Ruby Best Practices and Tips by Toptal Developers

This resource contains a collection of Ruby best practices and Ruby tips provided by our Toptal network members. As such, this page will be updated on a regular basis to include additional information and cover emerging Ruby techniques. This is a community driven project, so you are encouraged to contribute as well, and we are counting on your feedback.

Ruby is a unique language, full of quirky syntax and semantics that can sometimes seem a bit magical. While Ruby is often overshadowed by a certain web-development framework built on top of it, the language itself is versatile and well-suited to a wide variety of applications. Great Ruby developers can use Ruby to build anything from the back-side of a web application to command line utilities on your computer. The language is dynamic, reflective, and object oriented, but still requires experience for a developer to be able to harness their true power. Our best tips and practices are trying to fill the gap.

Check out the Toptal resource pages for additional information on Ruby. There is a Ruby hiring guide, Ruby job description, and Ruby interview questions.

How to Quickly Color Terminal Print Outputs?

Ruby has some great gems for coloring output prints to terminal, but sometimes you just need to print a couple of times and don’t want to add another gem just for that.

For example, instead of:

puts "\e[31mThis is red!\e[0m"
puts "\e[32mThis is green!\e[0m"

you could just create the format separated on variables, using %s placeholder (printf format):

red = "\e[31m%s\e[0m"
green = "\e[32m%s\e[0m"

Then use the % operator for readable formatting:

puts red % 'Red Again!'
puts green % 'Green Again!'
puts 'Mixed %s with %s!' % [red % 'red', green % 'green']

Let’s take a look at another example:

a = 2
b = 3
sum = a + b
format = '%s + %s = %s'
puts format % [red % a, red % b, green % sum]

This example is much more readable then the following code:

puts "\e[31m#{a}\e[0m + \e[31m#{b}\e[0m = \e[32m#{sum}\e[0m"

This can be used in a simple and unobtrusive way, and in a single place, like a log method:

class MyClass
  def log(message, level)
    red = "\e[31m%s\e[0m"
    green = "\e[32m%s\e[0m"

    puts "%s: %s" % [red % level, green % message]
  end
end

If you want to use this method globally, you can monkey patch String class:

class String
  def red
    "\e[31m#{self}\e[0m"
  end

  def green
    "\e[32m#{self}\e[0m"
  end
end

and then use them anywhere in the code:

puts "Red again!".red
puts "Green again!".green
puts "Mixed %s with %s!" % ["red".red, "green".green]

Let’s reuse the same example we used before, but now with the new syntax:

a = 2
b = 3
format = "%s + %s = %s"
puts format % [a.to_s.red, b.to_s.red, (a + b).to_s.green]

A more polite variation of monkey patching would be by using a module:

module ConsoleColors
  def red
    "\e[31m#{self}\e[0m"
  end

  def green
    "\e[32m#{self}\e[0m"
  end
end

String.include ConsoleColors

If you still want to use it globally but don’t want to monkey patch String, you can use the following code variation:

class Color
  # constants
  RED = "\e[31m%s\e[0m"
  GREEN = "\e[32m%s\e[0m"

  # or methods
  def self.red(s)
    "\e[31m#{s}\e[0m"
  end

  def self.green(s)
    "\e[32m#{s}\e[0m"
  end
end

puts Color::RED % 'reddish'
puts Color::GREEN % 'greenish'

puts Color.red 'reddish method'
puts Color.green 'greenish method'

In the end, you could use Ruby 2.0 refinements to limit the scope of the change:

module ConsoleColorsStringRefinement
  refine String do
    def red
      "\e[31m#{self}\e[0m"
    end

    def green
      "\e[32m#{self}\e[0m"
    end
  end
end

class MyLogClass
  using ConsoleColorsStringRefinement

  def log(message, level)
    puts "%s: %s" % [level.to_s.red, message.to_s.green]
  end
end

MyLogClass.new.log('My Message', 'DEBUG') # DEBUG: My Message

Contributors

  • 3eca00a0d3f5725d81723b69621287bdMarcos NevesFull stack web developer @ Freelancer
Like what you're reading?
Get the latest updates first.
No spam. Just great engineering and design posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

What is a Symbol Garbage Collector?

Ruby 2.2. introduced a big new feature, Symbol garbage collector (GC). Why is this big? Before this feature, in older Ruby versions prior to Ruby 2.2, symbols would live forever.

Let’s see what it means with the following example:

# Ruby 2.1
before = Symbol.all_symbols.size
100_000.times do |i|
  "toptaller_#{i}".to_sym
end
GC.start
after = Symbol.all_symbols.size
puts after - before
# => 100001

# Ruby 2.2
before = Symbol.all_symbols.size
100_000.times do |i|
  "toptaller#{i}".to_sym
end
GC.start
after = Symbol.all_symbols.size
puts after - before
# => 1

Just make sure to use any Ruby implementation > 2.2.0 to get this lovely new feature.

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering and design posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

How to Use Boolean Operators on Non Boolean Values?

As you might know, using ||= will update the variable with the value returned expression on the right side if the variable is undefined. Most of us know that already, but what you might not know about is the &&=.

To explain how it works, let’s take a look at the following simple code snippet:

str &&= str + "Talent" #=> nil
str = "Top"
str &&= str + "Talent" #=> "TopTalent"

As you can see from the example above, str &&= str + "Talent" is the equivalent of (str = str + "Talent") if str.

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering and design posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

Practice and Join The Ruby Community.

Toptal engineers are expected to continually improve their skills. Possible ways for keeping your Ruby skills sharp are:

  • Read books. One of my favorite books is Outliers by Malcolm Gladwell. In the book the author talks about 10,000 hours rule.
  • Keep writing code and practicing. You can check out the source code of your favorite libraries and try to pair with as many good developers as possible.
  • Join one of your local Ruby groups. The Ruby community is extremely helpful and accommodating towards newbies.
  • Don’t be shy to use Ruby’s IRC channel #ruby-lang.
  • Last, but not least, try to enjoy your work.

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering and design posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

Keep Your Constants in One Place.

Data that does not change during the application execution are called constants. These include the name of the application, administrator email, and the tagline. Ruby best practice is to keep all constants in one file. By using this convention, all developers on a project know exactly where to look for the constants and can quickly make changes. An example file would look like this:

# File config/settings.rb

ADMIN_EMAIL  = 'eqbal@toptalcom'
ADMIN_PASS   = 'my_awesome@pass'

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering and design posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

How to Access Data After the '__END__' Block?

In Ruby, the __END__ keyword is used to tell the parser to stop executing the source file. It is often used to store code snippets and notes about the script that weren’t really needed inline, and for appending documentation such as a license file to the end of a source code file.

Content of the file following the __END__ keyword is available via the global IO object named DATA. It contains all the content after __END__ in that Ruby script file.

How can this be useful? Let’s try it out. In the example below I’ll use it for quick scripts where I need to process text data, rather than piping to STDIN.

DATA.each_line.map(&:chomp).each do |url|
    `open "#{url}"`
end

__END__
https://www.toptal.com/
www.toptal.com/ruby/tips-and-practices

Not that useful? Ok, let’s say you have a bunch of CSV data where you want only one column from, and you want to use that CSV data and DATA to pull out the field you want:

require "csv"

CSV.parse(DATA, headers: true).each do |row|
  puts "#{row['Name']} => #{row['URL']}" 
end

__END__
Id,Name,URL
1,Eqbal,https://www.toptal.com/resume/eqbal-quran
2,Diego,http://https://www.toptal.com/resume/diego-ballona

This is pretty cool. Still don’t believe me? Consider the last example. Let’s say you just want to use DATA to contain ERB templates:

require 'erb'

number = rand(100)
erb = ERB.new(DATA.read)
puts erb.result()

__END__
Here is a randon number: <%= number %>.

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering and design posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

Avoid Using Evil 'rescue Exception => e'

Let’s say you want to capture exception details, like in the following example:

def do_something!
  # ... do something ...
  success
rescue Exception => e
  failed e
end

This is a terrible idea. Why? Exception is the root of the exception class hierarchy in Ruby. Everything from signal handling to memory errors will raise a subclass of Exception. You can check list of exceptions from ruby-core that we’ll rescue when rescuing Exception.

SystemStackError
NoMemoryError
SecurityError
ScriptError
  NotImplementedError
  LoadError
    Gem::LoadError
  SyntaxError
SignalException
  Interrupt
SystemExit
  Gem::SystemExitException

Do you want to rescue Exception in general like NoMemoryError? Let’s say this is a mailer script, do we need to email the user when the job fails?

What to do instead? Start using rescue => e as it’s the same for rescue StandardError => e and is almost certainly the broadest type of Exception that we want to rescue.

def do_something!
  # ... do something ...
  success
rescue => e
  failed e
end

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering and design posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

Use Splat Expander on Objects Other than Arrays.

Splat operator, *, in Ruby is used to simplify the code. For example, to coerce values into arrays and assign them to variables in a simple way, we can put star (splat operator) before the values, like in the following code snippet:

t,o,p,t,a,l = *(1..6) #=> [1, 2, 3, 4, 5, 6]
puts p #=> 3

The trick here is that we can also use splat expander on objects other than arrays:

Toptaller = Struct.new(:name, :tech)
toptaller = Toptaller.new("Eqbal", "Ruby")
name, tech = *toptaller
puts tech #=> Ruby

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering and design posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

How to Convert Any Base Number in Ruby?

Need to convert numbers from decimal to hexadecimal or binary? Good news. You can convert to any base from 2 to 36 in just one line in Ruby.

Using the Fixnum#to_s method, you can quickly convert any Fixnum object to the textual format of another base:

5.to_s(2) #=> "101"
5.to_s(3) #=> "12"
5.to_s(8) #=> "5"

# To convert back

"12".to_i(3) #=> 5

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering and design posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

Why Should You Avoid Using 'nil'?

What is nil? It is an instance of NilClass and a specialized kind of Ruby singleton. So NilClass is a class and nil is the one and only instance of that class.

>> nil.class
=> NilClass
>> nil.singleton_class
=> NilClass

The problem is that when we use nil more and more it’ll be easy to treat nil as something other than a straightforward object. For example, it’s common to use nil to mean absence of a result to the senders of messages. Consider the following example:

class Toptaller
  attr_reader :name

  def self.find(id)
    return nil if id == ''
    new(id)
  end

  def initialize(name)
    @name = name
  end
end

Toptaller.find('Eqbal') #=> #<Toptaller:0x007fb6b3635f58 @name="Eqbal">
Toptaller.find('')      #=> nil

Toptaller.find normally returns an instance of Toptaller, but when it receives an empty string for id it returns nil.

Let’s say we want to lookup Toptallers with ids = ['eqbal', '', 'diego']:

toptallers = ids.map {|id| Toptaller.find(id)}

toptallers.each {|top| puts top.name}
=> eqbal
=> NoMethodError: undefined method `name' for nil:NilClass

What to do now? Good idea is to use Null Object Pattern instead.

class Toptaller
  attr_reader :name

  def self.find(id)
    return nil if id == ''
    new(id)
  end

  def initialize(name)
    @name = name
  end

  def self.get_toptaller(name)
    case name
    when nil
      NilToptaller.new
    else
      Toptaller.find(name)
    end
  end
end

names = ["Eqbal", nil, "Diego"]

class NilToptaller
  def name
    "No name."
  end
end

toptallers = names.map {|n| Toptaller.get_toptaller(n).name}
["Eqbal", "No name.", "Diego"]

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering and design posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

Write Readable Code.

If we consider code readability, code maintainability and code efficiency, in Ruby code readability is the most important.

If you have a choice in writing your code between clever, short, and maybe faster code versus longer but readable code, go for readability. Ruby makes it easy to write really bad code that people would fear to touch with a long pole. At the same time, Ruby also lets you write beautiful and concise code.

This can be confusing once you start working with Ruby. But when you spend more time reading different Ruby libraries and other developers’ code you will start seeing some patterns among Ruby experts’ code. Learn from the best and start writing better and more readable code.

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering and design posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

How to Track Running Process?

Let’s say you want to track your running processes by using using ps command. Wouldn’t it be great if you could name your process so you can easily track it? The good news is that you can. The trick is to assign a string to the global variable $PROGRAM_NAME:

echo '$PROGRAM_NAME = "Toptal"; sleep 5' >> my_processs.rb
ruby my_processs.rb
ps -A | grep Toptal

This trick can be also helpful if you want to share the status of your process rather than logging the information.

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering and design posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

Using Ruby Interactive to Optimize Your Ruby Documentation

Ruby Interactive, or ri for short, is a tool to navigate documentation and display information about Ruby classes, all from the console like a Ninja. It is a companion to RDoc, a documentation system.

If you put comments in your program files in the prescribed RDoc format, RDoc will scan your files, extract those comments, organize them intelligently, and in the end will create nicely formatted documentation based on them.

So why to bother using it? For one, it’s already installed with Ruby. With Ruby Interactive you can access all your documentation and Ruby information offline, and in a faster and better way than searching for documentation through Google. Most of all, it looks more in the Ruby way.

Let’s see how this works on a few examples:

ri Array::wrap # class method lookup
ri Array#max # instance method lookup

$ ri Enumerable.each
Enumerable.each not found, maybe you meant:

Enumerable#each_cons
Enumerable#each_entry
Enumerable#each_slice
Enumerable#each_with_index
Enumerable#each_with_object

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering and design posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

Why Should You Gemify Your Shared Libraries?

As you work with Ruby a lot and develop applications, you will start using more and more components, like fast RESTful APIs or any other frameworks. In that situation you need to think about separating responsibilities and business logic. For example, if you are using Ruby on Rails, you should start using ActiveRecord and keep all rake tasks outside the Rails.

Let’s now imagine you want to create a new restful API using Grape, but you don’t want to end up replicating models. What can you do?

One way to do this is to move your models and core tasks to a Gem so it can be decoupled and used anywhere. Another way is to use Git Submodules. Since encapsulating some common code into a Gems is considered good practice nowadays, we’ll stick with using Gems in this article.

You can use Bundler to initiate a new Gem with the command: bundle gem core. Bundler will generate basic file structure that looks like this:

├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── core.gemspec
└── lib
    ├── core
    │   └── version.rb
    └── core.rb

These files contain most of the business logic, and we need to move them to a separate Gem so they can be used outside Rails applications:

  • Move your db folder to the gem: mv /path_to_rails/db /path_to_gem
  • Move your tests folder to the gem: mv /path_to_rails/spec /path_to_gem
  • Move your lib/tasks to the root path of the gem: mv /path_to_rails/lib/tasks /path_to_gem
  • Move your models to lib folder in the gem mv /path_to_rails/app/models /path_to_gem/lib/gem_name/
  • Move config folder mv /path_to_rails/config /path_to_gem/
  • Update your Gemfile accordingly
  • Modify the Rakefile to use ActiveRecord Rake tasks, something like:
require 'rake'
require 'active_record'

include ActiveRecord::Tasks
DatabaseTasks.database_configuration = YAML::load(File.open('./config/database.yml'))
DatabaseTasks.db_dir = 'db'
DatabaseTasks.create_current(ENV['RACK_ENV'])

That’s it. Now in your Grape API project you can just include the Gem in Gemfile something like gem 'core', git: '/path/to/my/gem', and you are all done.

Please note that we used Ruby on Rails in this example, but the logic and files structure can be applied to any Ruby framework.

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering and design posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

Recommended Ruby IDEs and Editors

Some developers like to use IDEs for automatic code generation, autocomplete, navigating, integrated debugging, and more. There is no official IDE for Ruby but RubyMine could be the closest to that since it’s widely supported and used. Nevertheless, most of the Ruby developers are using editors, because of the simplicity and speed that they offer.

Here is quick review of editors that can be used with Ruby:

  • RubyMine: As mentioned, this is an IDE rather than an editor. Although Ruby does not need IDE for building applications in the way Java or C++ do. Why? First, Ruby is an interpreted language and its code is not required to be compiled. Second, because of Ruby’s nature and Ruby on Rails by extension, in general it tries to be very simple and natural to write. As for an example, config files in Ruby on Rails are just Ruby files. There is no new syntax to learn, and you can use almost any construct available in the language without wasting much time learning the IDE.
  • Sublime Text: This is my favorite editor. It has a smooth UI and a huge community, and has better performance than most of the other editors. Just don’t forget to install and use its corresponding Ruby package.
  • Textmate: Very popular with Mac users. DHH, the creator of Rails framework, and Ryan Bates, the producer of RailsCasts among many others prefer this editor.
  • Vim: It is a must-know editor, or at least you need to know the basics if you will ever need to edit your files remotely. Most probably at one time in your Ruby developer career you will need SSH to your server and you’ll need to edit some files. It could be extremely fast and productive if you are willing to spend some time to learn the shortcuts. Just don’t forget to use the right set of plugins to be productive. There is the popular Janus: Vim Distribution and Srushti’s Vim setup.
  • Emacs: A popular Vim alternative. You want to customize it and make it Rails friendly? Don’t forget to check Emacs Wiki. Need another reason to use it? Matz is using it.

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering and design posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

How to Test a Set of Very Large Numbers

Let’s say you need to test a set of very large numbers, even infinity, in Ruby. What would be the best way to do that? You can use Range object as an infinite lazy list.

You just need to be careful when using methods like #select, because you might end up with an infinite loop while #select keeps checking every number because it never reaches Infinity.

In such cases, you can use the Enumerable#lazy method to create a lazy enumerator and then use the #first method to take a certain number of items from that list.

The following examples show how can this be achieved:

infinity = 1.0 / 0

(1..infinity).include?(100_000_000)  #=> true
(1..infinity).include?(-100_000_000) #=> false
(100_000_000..infinity).step(100_000_000).take(2) #=> [100000000.0, 200000000.0]

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering and design posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

Use Formatting Interpolation Technique to Format Decimals And Modify Strings Easily

To ease creation of the formatted string data, Ruby uses the string format syntax, or the % operator. This operator enables us to replace certain parts of our strings at runtime with the actual value. After the % operator, we specify arguments. % takes either a single element or an array of elements as its sole argument.

Here are few examples to show the % operator in action:

>> gpa = 3.8
=> 3.8
>> "%.2f" % gpa
=> "3.80"
>> '%d - %d' % [50, 50]
=> '50 - 50'
>> "%.5f" % gpa
=> "3.80000"
>> "**%s**" % "Toptal"
=> "**Toptal**"

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering and design posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

How to Create a Hash from a List of Values

In Ruby, there is a simple way of creating a hash from a list:

  a = %w{ T O P T A L }      
  Hash[*a] #=>  {"T"=>"O", "P"=>"T", "A"=>"L"}
  Hash[ [["dev","eqbal"], ["skil","ruby"] ]] #=> {"dev"=>"eqbal", "skil"=>"ruby"}

Let’s consider more real life examples and let’s imagine the following scenario where you’ve just collected some data. Collected data is in tabular format and organized so one array represents column headers, and the other array of arrays represents the rows values.

columns = [ "Name", "technology", "availability"]

rows = [ 
  [ "Eki",   "ruby",    true ], 
  [ "Dave",  "angular", false], 
  [ "Aamir", "cpp",     false ]
]

The columns array holds the column names. These will be the keys for the final hashes. The rows array is an array of arrays, holding the actual tabular data. The objective, once again, is to end up with an array of hashes where each hash’s keys will be the column names, and value as row.

# The hard way 
results = []

for i in (0...rows.length)  
  h = {}
  for j in (0...columns.length)    
    h[columns[j]] = rows[i][j]    
  end
  results << h
end

results # =>  [{"Name"=>"Eki", "technology"=>"ruby", "availability"=>true}, {"Name"=>"Dave", "technology"=>"angular", "availability"=>false}, {"Name"=>"Aamir", "technology"=>"cpp", "availability"=>false}]

# Easier way 

correlated = rows.map{|r| Hash[ columns.zip(r) ] }

The Array#zip method will ‘zip’ two arrays together, making an array of arrays, where each array is holding the elements of the two component arrays at the same index.

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering and design posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

Why Is ‘Enumerable’ Method Better than ‘For’ and ‘While’ Loops?

Many Ruby developers use For and While loops. It is a better practice to use enumerable methods, like: #each, #map, #select, #inject, #reject and #detect instead to iterate through an array or objects. Not to mention that using #each is considered a more idiomatic use of Ruby.

Let’s show this with an example:

loop1 = []
loop2 = []

arr = ["top", "tal", "ent"]

for c in arr
  loop2 << c
end

# This is more idiomatic ruby code
arr.each do |c|
  loop1 << c 
end

In the end this approach makes the code more readable, and more in a Ruby way.

Contributors

Submit a tip

Fields marked with an asterisk (*) are required
Thanks for submitting your tip proposal
Our editorial staff will review it shortly. Please note that tips proposals are subject to review and editing, and may or may not be selected for posting, at the sole discretion of Toptal, LLC.