10 Ruby Tricks to Make Your Code Nearly Perfect

Written by Kevin Liew on 26 Oct 2015
29,297 Views • Web Development

Ruby is awesome! If you think it’s not, you just haven’t got an opportunity to explore the simplicity, elegance and power of Ruby.

A lot of web services and enterprise level platforms use Ruby. Which ones? Twitter, Shopify, Crunchbase, Groupon. What about software? Top orchestrators - Puppet and Chef - are written in Ruby. Millions of user and thousands of developers love this language. And to love it even more, let’s go through some tips and tricks that will make your Ruby code beautiful. Here are tricks provided by Ruby on Rails develompent company RW.

1. Organize your list of values into a hash

Say, you have a list of values, and to make it easy to process them, why not create a hash? The syntax is pretty straightforward - Hash[...]. Here’s a short example:

Hash['key1', 'value1', 'key2', 'value2']

# => {"key1"=>"value1", "key2"=>"value2"}

2. Lambda Literal ->

Lambda expressions is the new big thing in Java 8. Ruby’s not lagging behind. Lambdas are an excellent way to define scopes in Ruby and if you did not know that you do now! Welcome, Lambda Literal ->

a = -> { 1 + 1 }
a.call
# => 2

a = -> (v) { v + 1 }
a.call(2)
# => 3

3. What Double stars (**) can do

The double star is a neat little trick in Ruby. See the following method:

OK, let’s take a look at the below method. It features a double star:

def my_method(a, *b, **c)
  return a, b, c
end

Who is who here? It’s clear enough that ‘a’ is just an ordinary parameter. Nothing fancy here. *b is commanded to take all parameters after the first one. These parameters will be saved in an array. **c accepts all parameters passed after the method call (at the end of it).

Here are some examples to illustrate it:

One parameter

new_method(1)
# => [1, [], {}]

1+ parameters

new_method(1, 2, 3, 4)
# => [1, [2, 3, 4], {}]

Several parameters and hash-style parameters

new_method(1, 2, 3, 4, a: 1, b: 2)
# => [1, [2, 3, 4], {:a=>1, :b=>2}]

As you can see, you can easily organize data in a way that makes it easy to process and re-use in your code.

4. Arrays and objects should be handled exactly the same way

Sometimes you might want to give the option to either accept a single object or an array of objects. Instead of checking for the type of object you’ve received, you could use [*something] or Array(something).

In your code you may choose if or not you want to accept an array. Sometimes, you should only process a single object. Of course, you may check what kind of object you have received. But that’s an extra line of code and additional logic.

Here are two examples of working with arrays and single objects. First off, let’s define variables.

variable = 1
variable _arr = [1, 2, 3]

Now, let’s loop through using [*...]:

[*variable ].each { |s| s }
[*variable _arr].each { |s| s }

It’s time to work with an array - Array(...).

Array(variable ).each { |s| s }
Array(variable _arr).each { |s| s }

5. Double Pipe Equals ||=

The Double Pipe Equals is a great tool to write concise code. Double pipe equal is a cool thing! Some examples never hurt:

a || a = b # Correct

However, some folks will think of this one:

a = a || b # Wrong

Why is it wrong? Well, why reassign variable a? We have it already!

Now, let’s perform some calculations:

def total_sum
  @total ||= (2..10000000).to_a.inject(:+)
end

Now you could have other method calling total to get the total value but it will only be calculated the first time.

This method will perform calculation only having been called the first time! How cool is that, huh?

6. Ask for parameters to be accepted: Mandatory hash parameters

This is a Ruby 2.0 feature. The traditional way to define a method that take a hash of parameters is the following:

def new_method({})
end

You can specify the keys that you are waiting for and even define default values for them! a and b are mandatory keys.

However, you may go further and specify what keys and default values such a method expects:

def new_method(a:, b:, c: 'value1')
  return a, b, c
end

Let’s call this method with various parameters passed over:

new_method(a: 2)

# => ArgumentError: missing keyword: b

Why didn’t the interpreter complain over missing c? It’s because it already has a predefined value in the method declaration. Here’s an example:

new_method(a: 2, b: 3)
# => [2, 3, "value1"]

And now let’s pass all 3 values:

new_method(a: 1, b: 2, c: 3)
# => [1, 2, 3]

There’s a more elegant way to pass parameters, for instance saving them to a hash and then passing this hash to the method:

hash = { a: 1, b: 2, c: 3 }
new_method(hash)
# => [1, 2, 3]

7. How to easily generate numbers or alphabet

You might want to generate a list of numbers or put the entire alphabet inside an array. Well, you can use ruby ranges to do this.

For some reason, you may need to generate an alphabet or just a pre-defined list of numbers. Defining a range in Ruby is a piece of cake.

English alphabet:

('a'..'z').to_a
# => ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]

Number from 1 to 11

(1..11).to_a
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

8. Improving your code with taps

A tap can make your code more readable. Let’s create a simple class:

class User
  attr_accessor :a, :b, :c
end

Now, let’s define a method that creates a new user with a sect of attributes. Here’s a traditional way:

def new_method
  o = User.new
  o.a = 1
  o.b = 2
  o.c = 3
  o
end

Or you could use tap to do it like this.

def new_method
  User.new.tap do |o|
    o.a = 1
    o.b = 2
    o.c = 3
  end
end

The object being called is saved to a block and is then returned.

9. Default value for hash

If some value is not defined in a hash and you attempt to access it, you’ll get nil. However, this behavior can be changed. Make sure you do it with caution!

Here’s an example of defining the default value to be 0, so we get 0 back but not nil.

a = Hash.new(0)
a[:a]
# => 0

Easy! You can define anything here. An empty hash? No problems!

a = Hash.new({})
a[:a]
# => {}

Maybe a random string? OK, let’s do it:

a = Hash.new('rubyrules’’)
a[:a]
# => rubyrules"

10. heredocs: how to make your code prettier

EOT is sensitive to leading spaces. So, your code will look like this:

def my_method
  <<-EOT
Hello
World
  EOT
end

That works, but there’s a way to better format this chunk of code. Let’s use gsub method with a simple regex that will remove leading spaces. Automation rules:

def my_method
  <<-EOT.gsub(/^\s+/, '')
    Hello
    World
  EOT
end

Sure, this is just a small token of knowledge that you can get on Ruby. But hopefully, it should be a good start!

Join the discussion

Comments will be moderated and rel="nofollow" will be added to all links. You can wrap your coding with [code][/code] to make use of built-in syntax highlighter.

3 comments
youjing 8 years ago
For hashes with a default value(array in this case), it should be
Hash.new { |h, k| h[k] = [] }

more explanation here
http://stackoverflow.com/questions/2698460/strange-behavior-when-using-hash-default-value-e-g-hash-new#answer-28916684
Reply
Jeroen Weeink 8 years ago
Nice post! I already completely forgot about the double splat arguments, thanks for bringing that one up!

There's one slight caveat in point 9 though. When passing a mutable object into Hash::new, that one instance will be used as the default value.

This can lead to some side effects:

a = Hash.new({})
a[:a] # => {}
a[:a][:b] = "c"
a[:b] # => {:b=>"c"}


To avoid this, use a default proc, which creates a new hash for each missing key:

a = Hash.new { |hash, key| hash[key] = {} }
a[:a] # => {}
a[:a][:b] = "c"
a[:b] # => {}
Reply
NilColor 8 years ago
a = Hash.new(0) is not a good idea =)


a = Hash.new({})
a[:a] # => {}
a[:a][:yay] = "wat"
a[:b] # => {:yay=>"wat"}

# =========================
# better
# =========================

a = Hash.new { Hash.new }
a[:a] # => {}
a[:a][:yay] = "wat"
a[:b] # => {}
Reply