Ruby is an interesting language. But when it comes to comparing it to giant classic languages like C or Java, it’s actually quite new. For those who are familiar with these classic languages, it’s not so simple to approach Ruby’s syntax even though it’s language is so close to the verbal language.
In addition, because of its flexibility, you may not know which symbol to use or you may not even know if it exists. Today, we’ll review some tips that Ruby ninjas use in order to optimize their code. After reviewing this, we’d recommend using them in your daily coding or at least not to be confused when reading Ruby code from Ruby experts.
Shall we begin?
The Basics
Basic shortcuts
First, in Ruby, there are some succinct ways to create some kind of object like [] to create array or {} to create Hash (or block/proc). Other than that, we can:String
%{} or %Q{} are different ways to represent a double quote string and they can store string interpolation. By using this way, you don’t need to care about the annoying double quote character (“) like working with a standard string and a double quotation:irb(main):001:0> %{Hi! I am a string!} => "Hi! I am a string!" irb(main):002:0> %Q{Me too! "_"} => "Me too! \"_\"" irb(main):003:0> %{You're 5. I am older than you 2 years old, so I am #{5+2} years old.} => "You're 5. I am older than you 2 years old, so I am 7 years old."On the other hand, %q{} is equivalent to a single quote string. It means that you cannot use string interpolation when it comes to age calculation like the above example.
irb(main):004:0> %q{#{2 + 5} equals to?} => "\#{2 + 5} equals to?"
Array
%w and %W can be used as what String#split can do. It splits a string into words by spaces. If you want to escape some spaces, you can use \ character like escaping a quotation character from a string. The difference between %W and %w is that %W allows string interpolation.irb(main):005:0> %w{Fruits\ I\ like: oranges apple banana} => ["Fruits I like:", "oranges", "apple", "banana"] irb(main):006:0> %W{I\ ate #{1+8}\ apples\ today} => ["I ate", "9 apples today"]
Regex
Usually, // is used to express a Regular Expression but if you want to escape the / character, you can use %r as below:irb(main):001:0> %r{%d/%d} => /%d\/%d/
Symbol
Similar to the above example, we can use %s to create symbols instead of : or :”” to escape special characters. One note, %s does not support string interpolation.irb(main):002:0> %s{studyId} => :studyId irb(main):003:0> %s{hi:man} => :"hi:man" irb(main):004:0> %s{id#{1+2}} => :"id\#{1+2}" irb(main):005:0> :"id#{1+2}" => :id3
Shelling out
When text is wrapped in backquotes (or backticks), the text inside of it is passed to a special function named Kernel.`. This function executes the content inside the quotes like in a shell command and returns a string of results. Instead of using backticks you can use %x instead. It supports string interpolation as well.irb(main):006:0> `echo welcome 2019!` => "welcome 2019!\n" irb(main):007:0> `echo welcome #{2018+1}!` => "welcome 2019!\n" irb(main):010:0> %x{echo welcome #{2018+1}!} => "welcome 2019!\n"
Lambda literal
This is a simple way to define scope in Rails by using ->:irb(main):013:0> add = -> (a, b) { a + b } => #<Proc:0x007f80028cce98@(irb):13 (lambda)> irb(main):015:0> add.call(1, 2) => 3
Sneak Peek
Something more interesting
We will now give you a sneak peek into some Ruby tricks that you may not have come across yet if you haven’t been coding Ruby for very long. If you master these tricks, you will be sure to distinguish your Ruby abilities from the rest of the pack.Parameter with *, ** prefix
Take a look at this function:def my_func a, *b, **c return a, b, c endIn this function, a is a normal parameter, With b, all arguments get passed to the function except a, which will be stored in b as an array. And finally with c, if you pass arguments by key-value type at the end of the function calling, they will be stored in c:
irb(main):001:0> def my_func a, *b, **c irb(main):002:1> return a, b, c irb(main):003:1> end => :my_func irb(main):004:0> my_func 1, 2, 3, 4, a: 5, b: 6 => [1, [2, 3, 4], {:a=>5, :b=>6}] irb(main):005:0>
Handle objects like an array
Sometimes, you need to handle input data with flexible types, maybe it’s an object or it’s an array. Normally, you need to check if it’s an array or not. But by using this trick, you can treat your object like a normal array with [*object] or [*array] or Array(something).irb(main):005:0> number = 1 => 1 irb(main):006:0> array = [2, 3, 4] => [2, 3, 4] irb(main):007:0> [*number].each { |s| s } => [1] irb(main):008:0> [*array].each { |s| s } => [2, 3, 4]
Tap method
tap is an interesting method. It’s used to make manipulations on objects before returning them. tap was created for tapping into method chains, it just allows you to do something with an object inside of a block, and to always have that block returned to the object itself. So the code:def something result = operation do_something_with result result endcan be turned to:
def something operation.tap do |op| do_something_with op end end
Copy Objects
Deep copy
When you copy an object, it means that you copy the reference to the copied object. As you can see:irb(main):009:0> fruits = %w(apple banana orange) => ["apple", "banana", "orange"] irb(main):010:0> new_fruits = fruits => ["apple", "banana", "orange"] irb(main):011:0> fruits.map(&:object_id) => [70109297139200, 70109297139180, 70109297139160] irb(main):012:0> new_fruits.map(&:object_id) => [70109297139200, 70109297139180, 70109297139160] irb(main):013:0> fruits.object_id => 70109297139120 irb(main):014:0> new_fruits.object_id => 70109297139120By using Marshall class, you can create an deep copy object:
irb(main):015:0> complete_new_fruits = Marshal.load Marshal.dump(fruits) => ["apple", "banana", "orange"] irb(main):016:0> complete_new_fruits.object_id => 70109305512340 irb(main):017:0> complete_new_fruits.map &:object_id => [70109305512320, 70109305512280, 70109305512260]For example, if I have an array of strings and I want to capitalize all the elements. Normally, you can write something like this:Using & to create procs
irb(main):018:0> fruits = ['banana', 'apple', 'orange'] => ["banana", "apple", "orange"] irb(main):019:0> fruits.map { |f| f.upcase } => ["BANANA", "APPLE", "ORANGE"]With & operator, it’s more elegant:
irb(main):021:0> fruits.map &:upcase => ["BANANA", "APPLE", "ORANGE"]The & operator takes the method caller (the fruits object), and call the to_proc method on that object. If that object is a symbol, the to_proc function will create a proc and call the method with the name corresponding to the symbol via send method. So the above example is equivalent to:
irb(main):023:0> fruits.map {|f| f.send(:upcase)} => ["BANANA", "APPLE", "ORANGE"]Now, instead of calling the instance method, we can do something like:
def demo_method(word) word + ' - ' + 'demo' end # Instead of: res = arr.map{|str| demo_method(str)} # We can: res = arr.map(&method(:demo_method))