Ruby Language

Topics related to Ruby Language:

Getting started with Ruby Language

Ruby is a multi-platform open-source, dynamic object-oriented interpreted language, designed to be simplistic and productive. It was created by Yukihiro Matsumoto (Matz) in 1995.

According to its creator, Ruby was influenced by Perl, Smalltalk, Eiffel, Ada, and Lisp. It supports multiple programming paradigms, including functional, object-oriented, and imperative. It also has a dynamic type system and automatic memory management.

Casting (type conversion)

Arrays

Classes

Class names in Ruby are Constants, so the first letter should be a capital.

class Cat # correct
end  

class dog # wrong, throws an error
end

Hashes

Hashes in Ruby map keys to values using a hash table.

Any hashable object can be used as keys. However, it's very common to use a Symbol as it is generally more efficient in several Ruby versions, due to the reduced object allocation.

{ key1: "foo", key2: "baz"  }

Blocks and Procs and Lambdas

Be careful about operator precedence when you have a line with multiple methods chained, like:

str = "abcdefg"
puts str.gsub(/./) do |match|
  rand(2).zero? ? match.upcase : match.downcase
end

Instead of printing something like abCDeFg, like you'd expect, it prints something like #<Enumerator:0x00000000af42b28> -- this is because do ... end has lower precedence than methods, which means that gsub only sees the /./ argument, and not the block argument. It returns an enumerator. The block ends up passed to puts, which ignores it and just displays the result of gsub(/./).

To fix this, either wrap the gsub call in parentheses or use { ... } instead.

Inheritance

Control Flow

Strings

Symbols

Advantages of using symbols over strings:

1. A Ruby symbol is an object with O(1) comparison

To compare two strings, we potentially need to look at every character. For two strings of length N, this will require N+1 comparisons

def string_compare str1, str2
  if str1.length != str2.length
        return false
  end
  for i in 0...str1.length
    return false if str1[i] != str2[i]
  end
  return true
end
string_compare "foobar", "foobar"

But since every appearance of :foobar refers to the same object, we can compare symbols by looking at object IDs. We can do this with a single comparison.(O(1))

def symbol_compare sym1, sym2
  sym1.object_id == sym2.object_id
end
symbol_compare :foobar, :foobar

2. A Ruby symbol is a label in a free-form enumeration

In C++, we can use “enumerations” to represent families of related constants:

enum BugStatus { OPEN, CLOSED };
BugStatus original_status = OPEN;
BugStatus current_status  = CLOSED;

But because Ruby is a dynamic language, we don’t worry about declaring a BugStatus type, or keeping track of the legal values. Instead, we represent the enumeration values as symbols:

original_status = :open
current_status  = :closed

3. A Ruby symbol is a constant, unique name

In Ruby, we can change the contents of a string:

"foobar"[0] = ?b # "boo"

But we can’t change the contents of a symbol:

:foobar[0]  = ?b # Raises an error

4. A Ruby symbol is the keyword for a keyword argument

When passing keyword arguments to a Ruby function, we specify the keywords using symbols:

# Build a URL for 'bug' using Rails.
url_for :controller => 'bug',
        :action => 'show',
        :id => bug.id

5. A Ruby symbol is an excellent choice for a hash key

Typically, we’ll use symbols to represent the keys of a hash table:

options = {}
options[:auto_save]     = true
options[:show_comments] = false

Exceptions

An exception is an object that represents the occurrence of an exceptional condition. In other words, it indicates that something went wrong.

In Ruby, exceptions are often referred to as errors. That's because the base Exception class exists as a top-level exception object element, but user-defined execution exceptions are generally StandardError or descendants.

Thread

Methods

A method is a named block of code, associated with one or more objects and generally identified by a list of parameters in addition to the name.

def hello(name)
  "Hello, #{name}"
end

A method invocation specifies the method name, the object on which it is to be invoked (sometimes called the receiver), and zero or more argument values that are assigned to the named method parameters. The value of the last expression evaluated in the method becomes the value of the method invocation expression.

hello("World")
# => "Hello, World"

When the receiver is not explicit, it is self.

self
# => main

self.hello("World")
# => "Hello, World"

As explained in the Ruby Programming Language book, many languages distinguish between functions, which have no associated object, and methods, which are invoked on a receiver object. Because Ruby is a purely object-oriented language, all methods are true methods and are associated with at least one object.

Overview of Method Parameters

TypeMethod SignatureCall ExampleAssignments
Requireddef fn(a,b,c)fn(2,3,5)a=2, b=3, c=5
Variadicdef fn(*rest)fn(2,3,5)rest=[2, 3, 5]
Defaultdef fn(a=0,b=1)fn(2,3)a=2, b=3
Keyworddef fn(a:0,b:1)fn(a:2,b:3)a=2, b=3

These argument types can be combined in virtually any way you can imagine to create variadic functions. The minimum number of arguments to the function will equal the amount of required arguments in the signature. Extra arguments will be assigned to default parameters first, then to the *rest parameter.

TypeMethod SignatureCall ExampleAssignments
R,D,V,Rdef fn(a,b=1,*mid,z)fn(2,97)a=2, b=1, mid=[], z=97
fn(2,3,97)a=2, b=3, mid=[], z=97
fn(2,3,5,97)a=2, b=3, mid=[5], z=97
fn(2,3,5,7,97)a=2, b=3, mid=[5,7], z=97
R,K,Kdef fn(a,g:6,h:7)fn(2)a=2, g=6, h=7
fn(2,h:19)a=2, g=6, h=19
fn(2,g:17,h:19)a=2, g=17, h=19
VKdef fn(**ks)fn(a:2,g:17,h:19)ks={a:2, g:17, h:19}
fn(four:4,five:5)ks={four:4, five:5}

method_missing

Always call super, at the bottom of this function. This saves silent failure when something is called and you don't get an error.

For example, this method_missing is going to cause problems:

class Animal
  def method_missing(method, *args, &block)
    say, speak = method.to_s.split("_")
    if say == "say"
      speak
    end
  end
end

=> Animal.new.foobar
=> nil # This should really be raising an error

method_missing is a good tool to use when appropriate, but has two costs you should consider. First, method_missing is less efficient -- ruby must search the class and all of its ancestors before it can fall back on this approach; this performance penalty may be trivial in a simple case, but can add up. Second and more broadly, this is a form of meta-programming that has great power that comes with responsibility to ensure that the implementation is secure, properly handles malicious inputs, unexpected inputs, and so on.

You should also override respond_to_missing? like so:

class Animal
  def respond_to_missing?(method, include_private = false)
    method.to_s.start_with?("say_") || super
  end
end

=> Animal.new.respond_to?(:say_moo) # => true

Numbers

Numbers hierarchy

Ruby includes several built-in classes to represent numbers:

Numeric
  Integer
    Fixnum    # 1
    Bignum    # 10000000000000000000
  Float       # 1.0
  Complex     # (1+0i)
  Rational    # Rational(2, 3) == 2/3
  BigDecimal  # not loaded by default

The most common are:

  • Fixnum to represent, for instance positive and negative integers
  • Float to represent floating point numbers

BigDecimal is the only one not loaded by default. You can load it with:

require "bigdecimal"

Note that in ruby 2.4+, Fixnum and Bignum are unified; all integers are now just members of the Integer class. For backwards compatibility, Fixnum == Bignum == Integer.

Iteration

Regular Expressions and Regex Based Operations

Comparable

x <=> y should return a negative number if x < y, zero if x == y and a positive number if x > y.

Gem Usage

Design Patterns and Idioms in Ruby

Loading Source Files

Range

Comments

Operators

Operators are methods

Most operators are actually just methods, so x + y is calling the + method of x with argument y, which would be written x.+(y). If you write a method of your own having semantic meaning of a given operator, you can implement your variant in the class.

As a silly example:

# A class that lets you operate on numbers by name.
class NamedInteger
  name_to_value = { 'one' => 1, 'two' => 2, ... }

  # define the plus method
  def + (left_addend, right_addend)
    name_to_value(left_addend) + name_to_value(right_addend)
  end

  ...
end

When to use && vs. and, || vs. or

Note that there are two ways to express booleans, either && or and, and || or or -- they are often interchangeable, but not always. We'll refer to these as "character" and "word" variants.

The character variants have higher precedence so reduce the need for parentheses in more complex statements helps avoid unexpected errors.

The word variants were originally intended as control flow operators rather than boolean operators. That is, they were designed to be used in chained method statements:

raise 'an error' and return

While they can be used as boolean operators, their lower precedence makes them unpredictable.

Secondly, many rubyists prefer the character variant when creating a boolean expression (one that evaluates to true or false) such as x.nil? || x.empty?. On the other hand, the word variants are preferred in cases where a series of methods are being evaluated, and one may fail. For example a common idiom using the word variant for methods that return nil on failure might look like:

def deliver_email
  # If the first fails, try the backup, and if that works, all good
  deliver_by_primary or deliver_by_backup and return
  # error handling code
end

Operators

Special Constants in Ruby

Modules

Module names in Ruby are constants, so they have to start with a capital letter.

module foo; end # Syntax error: class/module name must be CONSTANT

Ruby Version Manager

Gem Creation/Management

Constants

Constants are useful in Ruby when you have values that you do not want to be mistakenly changed in a program, such as API keys.

Variable Scope and Visibility

Class variables are shared in the class hierarchy. This can result in surprising behavior.

class A
  @@variable = :x

  def self.variable
    @@variable
  end
end

class B < A
  @@variable = :y
end

A.variable  # :y

Classes are objects, so instance variables can be used to provide state that is specific to each class.

class A
  @variable = :x

  def self.variable
    @variable
  end
end

class B < A
  @variable = :y
end

A.variable  # :x

rbenv

Environment Variables

Let get user profile path in a dynamic way for scripting under windows

Singleton Class

Singleton classes only have one instance: their corresponding object. This can be verified by querying Ruby's ObjectSpace:

instances = ObjectSpace.each_object object.singleton_class

instances.count            # => 1
instances.include? object  # => true

Using <, they can also be verified to be subclasses of the object's actual class:

object.singleton_class < object.class  # => true

References:

File and I/O Operations

Time

Queue

Destructuring

IRB

Enumerators

C Extensions

Struct

Metaprogramming

Dynamic Evaluation

instance_eval

Message Passing

Keyword Arguments

Keyword arguments were introduced in Ruby 2.0, and improved in Ruby 2.1 with the addition of required keyword arguments.

A simple method with a keyword argument looks like the following one:

def say(message: "Hello World")
  puts message
end

say
# => "Hello World"

say message: "Today is Monday"
# => "Today is Monday"

As a reminder, the same method without keyword argument would have been:

def say(message = "Hello World")
  puts message
end

say
# => "Hello World"

say "Today is Monday"
# => "Today is Monday"
2.0

You can simulate keyword argument in previous Ruby versions using a Hash parameter. This is still a very common practice, especially in libraries that provides compatibility with pre-2.0 Ruby versions:

def say(options = {})
  message = options.fetch(:message, "Hello World")
  puts 
end

say
# => "Hello World"

say message: "Today is Monday"
# => "Today is Monday"

DateTime

Before using DateTime you need to require 'date'

Truthiness

As a rule of thumb, avoid using double-negations in code. Rubocop says that double negations are unnecessarily complex and can often be replaced with something more readable.

Instead of writing

def user_exists?
    !!user
end

use

def user_exists?
    !user.nil?
end

JSON with Ruby

Implicit Receivers and Understanding Self

Monkey Patching in Ruby

Introspection

Monkey Patching in Ruby

Refinements

Refinements are scope lexically, meaning they're in effect from the time they're activated (with the using keyword) until control shifts. Usually control is changed by the end of a module, class, or file.

Monkey Patching in Ruby

Monkey patching, while convenient, has some pitfalls that aren't immediately obvious. Most notably, a patch like that in the example pollutes the global scope. If two modules both add Hash#symbolize, only the last module required actually applies its change; the rest are erased.

Furthermore, if there's an error in a patched method, the stacktrace simply points to the patched class. This implies that there's a bug in the Hash class itself (which there is now).

Lastly, because Ruby is very flexible with what containers to hold, a method that seems very straightforward when you write it has lots of undefined functionality. For instance, creating Array#sum is good for an array of numbers, but breaks when given an array of a custom class.

A safer alternative is refinements, available in Ruby >= 2.0.

Catching Exceptions with Begin / Rescue

Command Line Apps

Debugging

Pure RSpec JSON API testing

Recursion in Ruby

Installation

ERB

Conventions:

  • ERB as a template: Abstract business logic into accompanied helper code, and keep your ERB templates clean and readable for people without Ruby knowledge.
  • Append files with .erb: e.g. .js.erb, .html.erb, .css.erb, etc.

Introspection in Ruby

Generate a random number

Alias of Random::DEFAULT.rand. This uses a pseudo-random number generator which approximates true randomness

Getting started with Hanami

OptionParser

Splat operator (*)

Multidimensional Arrays

Enumerable in Ruby

Ruby Access Modifiers

Operating System or Shell commands

Exec:
Exec is very limited in functionality and when executed will exit the Ruby program and run the command.

The System Command:
The System command runs in a sub-shell instead of replacing the current process and returns true or nill. The system command is, like backticks, a blocking operation where the main application waits until the result of the system operation completes. Here the main operation never needs to worry about capturing an exception raised from the child process.

The output of system function will always be true or nil depending on whether or not the script has been executed without error. Therefore, every error while executing the script will not be passed to our application. The main operation never needs to worry about capturing an exception raised from the child process. In this case the output is nil because the child process raised an exception.
This is a blocking operation where the Ruby program will wait until the operation of the command completes before going on.
The system operation use fork to fork the current process and then execute the given operation using exec.

The backticks (`):
The backtick character is usualy located under the escape key on the keyboard. Backticks runs in a sub-shell instead of replacing the current process and returns the result of the command.
Here we can get the output of the command but the program will crash when an exception is generated.
If there is an exception in the sub-process then that exception is given to the main process and the main process might terminate if exception is not handled. This is a blocking operation where the Ruby program will wait until the operation of the command completes before going on.
The system operation use fork to fork the current process and then execute the given operation using exec.

IO.popen:
IO.popen runs in a sub-process. Here the sub-process standard input and standard output are connected to the IO object.

Popen3:
Popen3 allows you to access the standard input, standard output and standard error.
The subprocess's standard input and output will be returned into IO objects.

$? (same as $CHILD_STATUS)
Can be used with the backticks, system() or %x{} operations and will give the status of the last system executed command.
This might be usefull to access the exitstatus and the pid properties.

$?.exitstatus