Damien Gonot
Home Blog Notes About

Ruby

Homepage / Notes / Computer Science / Programming Languages / Ruby

Language Features

Strings

Can use both single quotes ' or double quotes ".

String interpolation

/!\ Must use double quotes

name = 'world'
str = "hello #{name}"
hello world

Append to string

str = 'hello '
str << 'world'
hello world

Freezing strings

str = 'hello'.freeze
str.frozen?
true
str = 'hello'.freeze
str << ' world'

=can't modify frozen String: "hello" (FrozenError)=

Frozen string literals

# frozen_string_literal: true This line placed at the beginning of a file will freeze all strings in the file.

See also: https://docs.ruby-lang.org/en/3.0.0/doc/syntax/comments_rdoc.html#label-Magic+Comments

String to chars

"Damien".chars
["D", "a", "m", "i", "e", "n"]

Split

"String to be split".split
["String", "to", "be", "split"]
"String,to,be,split".split(',')
["String", "to", "be", "split"]

Arrays

https://docs.ruby-lang.org/en/3.0.0/Array.html

Concatenation

a = [1, 2, 3]
b = [4, 5, 6]

a.push(*b)
[1, 2, 3, 4, 5, 6]

Appending

list = []

(1..5).each {|i| list << i}

list
[1, 2, 3, 4, 5]

Destructuring / Pattern Matching

[1, 2, 3] => [a, b, c]
c
3

Symbols

Symbols are immutable, unique identifiers represented by a name preceded by a colon (:).

:hello

You can easily convert a string to a symbol and vice-versa:

.to_s
name
"name".to_sym
:name

Hashes

https://ruby-doc.org/core-3.0.0/Hash.html Older syntax, "hash rocket":

h = { => 0,  => 1}
{:foo=>0, :bar=>1}

Newer JSON-like syntax

h = {0, 1}
{:foo=>0, :bar=>1}

Getting value from key

Using array-like notation
h = {0, 1}
h[]
0

Using fetch

h = {0, 1}
h.fetch()
0

Can provide a default:

h = {0, 1}
h.fetch(, "Oops")
Oops

"Deep" nested value

h = {{{'deep_nested_value'}}, 1}
h.dig(, , )
deep_nested_value

Delete Key

h = {0, 1}
h.delete() # this returns the deleted key's value, so 0 in this case
h
{:bar=>1}

Merging two hashes

a = {'value'}
b = {'another value'}
a.merge(b)
{:first=>"value", :second=>"another value"}
Using Double Splat Operator
a = {'value'}
b = {**a, 'another value'}
{:first=>"value", :second=>"another value"}

Getting Keys Only

h = {0, 1}
h.keys
[:foo, :bar]

Getting Values Only

h = {0, 1}
h.values
[0, 1]

Omitting values

Starting from Ruby 3.1

x = 8
y = 9

{x:, y:}
{:x=>8, :y=>9}

Slice

user = { 'Damien', 28, 'CEO' }
user.slice(, )
{:name=>"Damien", :age=>28}

Except

user = { 'Damien', 28, 'CEO' }
user.except()
{:name=>"Damien", :age=>28}

Transform Values

{true, false}.transform_values(&:!)
{:a=>false, :b=>true}

Iterate Over Hash

h = {0, 1}
h.each do |key, value|
  puts "#{key}: #{value}"
end
foo: 0
bar: 1

Any?

h = {0, 1}
h.any? { |key, value| value > 0 }
true

Compact

Removes any null values

h = {0, nil}
h.compact
{:foo=>0}

Empty?

h = {0, nil}
puts h.empty?

h = {}
puts h.empty?
false
true

Functions

def say_hello(name)
  puts "hello #{name}"
end

say_hello('Damien')
hello Damien

Optional Arguments

def greet_name(name = 'John Doe')
  puts "hello #{name}"
end

greet_name
greet_name('Damien')
hello John Doe
hello Damien

Keyword Arguments

def greet_name(greeting:, name:)
  puts "#{greeting}, #{name}"
end

greet_name('Damien', 'hi')
hi, Damien

Starting from Ruby 3.1

def greet_name(greeting:, name:)
  puts "#{greeting}, #{name}"
end

greeting = 'hi'
name = 'Damien'
greet_name(name:, greeting:)
hi, Damien

One-liner

Starting from Ruby 3.0

def increment(x) = x + 1
increment(42)
43

Iterators

https://docs.ruby-lang.org/en/3.0.0/doc/syntax/control_expressions_rdoc.html

While

x = 0

while x < 5
  puts x
  x += 1
end
0
1
2
3
4

Until

x = 0

until x == 5
  puts x
  x += 1
end
0
1
2
3
4

For

x = [1, 2, 3, 4, 5]

for i in x do
  puts i
end
1
2
3
4
5

Each

names = ['Bob', 'Joe', 'Steve', 'Janice', 'Susan', 'Helen']

names.each { |name| puts name }
Bob
Joe
Steve
Janice
Susan
Helen

Enumerables

https://docs.ruby-lang.org/en/3.0.0/Enumerable.html

Any?

x = ['a', 'b', 'c']

x.any?('a')
true
x.any?('d')
false

Min

[1, 2, 3].min
0

Max

[1, 2, 3].max
3

minmax

[1, 2, 3].minmax
[1, 3]

Sort

[3, 2, 1].sort
[1, 2, 3]

Filter

[1, 2, 3, 4, 5].filter {|i| i >= 3}
[3, 4, 5]

Each With Index

hash = Hash.new

['a', 'b', 'c'].each_with_index {|item, index|
  hash[index] = item
}

hash
{0=>"a", 1=>"b", 2=>"c"}

Each With Object

(1..10).each_with_object([]) {|i, a| a << i*2}
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

Map

[1, 2, 3, 4, 5].map {|i| i * 2}
[2, 4, 6, 8, 10]

Reduce

[1, 2, 3, 4, 5].reduce(:+)
15

Blocks, Procs and Lambdas

Blocks

A block of code that can be passed to a method.

Single-line syntax
[1, 2, 3].each { |num| puts num }
1
2
3
Multi-line syntax
[1, 2, 3].each do |num|
  puts num
end
1
2
3

Procs

double = Proc.new { |num| num * 2 }
double.call(5)
10

Lambdas

l = lambda { |x| x * 3 }
l.call(3)
9

A shorter syntax is available using ->:

l = ->(x) { x * 3 }
l.call(5)
15

Pattern Matching

Starting from Ruby 3.1

[1, 2] => _, x
x
2
{ 'Damien', 28 } => {name:}
name
Damien

Classes

https://docs.ruby-lang.org/en/3.0.0/Class.html

class Greeter
  def initialize(name)
    @name = name.capitalize
  end

  def salute
    return "Hello #{@name}!"
  end
end

# Create a new object
g = Greeter.new("world")

g.salute
Hello World!

What is @foobar?

The variable which name begins which the character `@', is an instance variable of self. Instance variables are belong to the certain object. Non-initialized instance variables has value nil.

attr_reader

To avoid having to call @name with the @, attr_reader can be used:

class Greeter
  attr_reader 

  def initialize(name)
    @name = name.capitalize
  end

  def salute
    return "Hello #{name}!"
  end
end

# Create a new object
g = Greeter.new("world")

g.salute
Hello World!

Methods

Greeter.instance_methods(false)
:salute:name

Modules

https://docs.ruby-lang.org/en/3.0.0/Module.html

module Greeter
  def self.salute
    return "Hello World!"
  end
end

# Output "Hello World!"
Greeter.salute
Hello World!

What is ::?

Allow to access items in modules or class-level items in classes. Example:

module SomeModule
    module InnerModule
        class MyClass
            CONSTANT = 4
        end
    end
end

SomeModule::InnerModule::MyClass::CONSTANT
4

You could access CONSTANT by: SomeModule::InnerModule::MyClass::CONSTANT

Time

Time.now
2021-05-31 21:31:48.883643 -0400

UNIX Timestamp

Time.now.to_i
1622511129

Fibers

https://noteflakes.com/articles/2021-10-20-explaining-ruby-fibers

https://brunosutic.com/blog/ruby-fiber-scheduler

Misc

Conditional Assignment Operator

a ||= b means: If a is undefined, nil or false, assign b to a. Otherwise, keep a intact.

IRB

Interactive Ruby Shell, the REPL of Ruby!

Gems

Rails

https://rubyonrails.org/ Web Framework

Cache

https://guides.rubyonrails.org/caching_with_rails.html To enable caching in dev: rails dev:cache

Low-Level Caching

https://guides.rubyonrails.org/caching_with_rails.html#low-level-caching Using Rails.cache.fetch, both reading and writing is taken care of.

reload!

reload! is a method to reload your application code in the current console session.

Sandbox

rails console --sandbox shortcut: rails c -s Any modifications will be rolled back on exit

Hanami

https://hanamirb.org/ Alternative to Rails

state_machines

https://github.com/state-machines/state_machines

Adds support for creating state machines for attributes on any Ruby class

https://blog.appsignal.com/2022/06/22/state-machines-in-ruby-an-introduction.html

Polars

https://github.com/ankane/polars-ruby

🔥 Blazingly fast DataFrames for Ruby, powered by Polars

Resources

https://github.com/seanlerner/ruby-and-rails-learning-plan

https://learnrubythehardway.org/book/ex13.html

Polished Ruby Programming: Build Better Software with More Intuitive, Maintainable, Scalable, and High-performance Ruby Code

by Jeremy Evans