Ruby Tips & Tricks: Part 1

Every day I use a huge number of different Ruby features and I take them for granted. It's not until you are thrown into the world of PHP or vanilla JavaScript that you realise quite how useful these features are. This is my list of the top 10 Ruby features which everyone should be using...

Ternary Operators

If you just want to do something simple, your standard if else block can be quite verbose:

# Your standard if/else block
if user.admin?  
  access_level = 'admin'
else  
  access_level = 'user'
end

# An if else using Ternary operators
access_level = user.admin? ? 'admin' : 'user'  

These are great for simple checks where you just need to return one value or another.

Instance Method Caching

When you're working with an instance of something, you may find that one of your methods performs an action which may take some time and would benefit from a little caching. Step up the ||= operator. The purpose of this operator is to return the variable or set it if it evalues to nil (or false).

class Person  
  def tweets
      @tweets ||= Twitter.get_tweets(self.username)
  end
end

person = Person.new  
person.tweets   # => Takes a few seconds and returns an array of tweets  
person.tweets   # => Returns the tweets which were looked up the first time the method was called on this instance.  

In this example, we have a method on our Person class which returns the users latest tweets. The Twitter.get_tweets action can take some time and probably won't change during the lifetime of our Person instance. Therefore, the first time the method is called we'll get the tweets and they'll be set in the @tweets instance variable and any future calls on the same instance will return the same array.

Comparing Arrays

If you've got two arrays of data which you want to work with - there are a number of "operators" you can use to do this. This is best explained with some code...

We'll start with the basics. If you've got two arrays and you wish to combine all the objects from both arrays into a single array, the + operator will come in handy.

[1,2,3] + [3,4,5]   #=> [1,2,3,3,4,5]

You'll see that 3 is in both source arrays and appears twice in the new array. If you wanted to only include unique array members, you can use a union.

[1,2,3] | [2,3,4]   #=> [1,2,3,4]

Identifying differences between two arrays is also as easy. This method will take away any items which exist in the second array, out of the first array and return the result. You can see in this example, that 3 was removed because it was in the second array but 1 and 2 remain as they are not.

[1,2,3] - [3,4,5]   #=> [1, 2]

Finally, determining which items are common in two arrays can be done using an intersection operator. This will return an array of items which exist in both arrays.

[1,2,3] & [2,3,4]   #=> [2,3]

String Replacement

Many applications have a requirement to examine a string and change various parts of it based on patterns. For example, if you wanted to support Emoji's, you'll need to look at a block of text and replace any occurances of :something: with an image. Say hello to String#gsub.

The gsub method normally will take two methods. Firstly, what you want to match and then what to replace it with. In it's most basic form, we're just going to replace the characters "day" with the word "evening"

string = "Good morning!"  
string.gsub("morning", "evening")  #=> "Good evening!"  

This is sometimes all you need but more often than not, you'll need to be more picky about how you choose what to replace. Fortunately, the method supports using a regular expression. In this example, we're going to replace what look like emojis with an HTML image tag. You'll notice we've used \1 to reference the name of the emoji from the regular expression (denoted by the presence of brackets around the \w+).

string = "Hello there :smile: - isn't it a lovely day? :flower:"  
string.gsub(/\:(\w+)\:/, '<img src="emojis/\1.png">')  #=> "Hello there <img src=\"emojis/smile.png\"> - isn't it a lovely day? <img src=\"emojis/flower.png\">"  

This isn't ideal because it'll match anything regardless of whether it's a valid emoji or not. We can fix it though. The gsub method also allows you to provide a block which will be evaluated and used to determine the new value for each item. Here, we'll check that each emoji exists in a EMOJIS array before handling the replacement. Otherwise, we'll just return the normal string.

emojis = ['smile']  
string = "Hello there :smile: - isn't it a lovely day? :flower:"  
string.gsub(/\:(\w+)\:/) do  
  if emojis.include?($1)
    "<img src='emojis/#{$1}'>"
  else 
    ":#{$1}:"
  end
end  #=> "Hello there <img src='emojis/smile'> - isn't it a lovely day? :flower:"  

(The g on gsub stands for greedy. This is because it will replace every occurance in your source string. If you know there's only ever going to be one replacement, use String#sub.)

Mutation Methods

The astute developer will notice various methods which end in an exclamation mark, for example downcase! or compact!. In the Ruby core, these methods mean that the current object will be mutated as opposed to a new object being created. As you can see here...

string = "Hello!"  
# A new string will be created been created and the original
# string will remain the same
string.upcase     #=> "HELLO!  
string            #=> "Hello!"

# However, if we use the mutation method. The original string will
# be changed.
string.upcase!    #=> "HELLO!" (or in this case nil if no chars are changed).  
string            #=> "HELLO!"  

Now, although this is a good practice which is employed by many developers you may come across libraries where this is not the case. Always check the documentation of the library your using to be sure how it works.

Next Time...

That's it for part one of my Ruby Tips & Tricks. In part two, I'll be looking at:

  • Arrays - flattening, compacting, uniqing
  • Enumerables - injecting & mapping
  • Creating clever and funky class methods

If there's anything else you'd like me to cover, do feel free to drop me a message on Twitter and I'll happily include it.