Language consists of a system:

  • Semantics studies the aspects of meaning.
  • Syntax studies the structure, principles and relationships.

Quick reference

Structure:

Read documentation

  • Documentation is a representation of the language (im)possibilities.
  • Documentation can be found at
  • ruby-lang.org search shows more results from the documentation, rubyapi.org shows less results.
  • Class methods are called on the class itself and are defined with self. Instance methods are called on an instance of a class and are defined without self.
  • :: is a scope resolution operator. It is used to reference a constant, module, or class defined within another class or module. It is documented as a class method.
  • # is a method call operator. It is documented as an instance method.

Debugging

For debugging use p instead of puts:

  • p (print the value of the expression, including the value of the expression)
  • pp (pretty print the value of the expression)
  • print (prints without trailing new line)
  • puts (prints expression and nil)

Reserved keywords

Keywords

__ENCODING__ 
The script encoding of the current file.

__LINE__
The line number of this keyword in the current file.

__FILE__
The path to the current file.

BEGIN
Runs before any other code in the current file. 

END
Runs after any other code in the current file. 

alias
Creates an alias between two methods (and other things). 

and
Short-circuit Boolean and with lower precedence than &&

begin
Starts an exception handling block. 

break
Leaves a block early. 

case
Starts a case expression. 

class
Creates or opens a class. 

def
Defines a method. 

defined?
Returns a string describing its argument. 

do
Starts a block.

else
The unhandled condition in case, if and unless expressions. 

elsif
An alternate condition for an if expression. 

end
The end of a syntax block. Used by classes, modules, methods, exception handling and control expressions.

ensure
Starts a section of code that is always run when an exception is raised. 

false
Boolean false. 

for
A loop that is similar to using the each method. 

if
Used for if and modifier if statements. 

in
Used to separate the iterable object and iterator variable in a for loop. It also serves as a pattern in a case expression. 

module
Creates or opens a module. 

next
Skips the rest of the block. 

nil
A false value usually indicating “no value” or “unknown”. 

not
Inverts the following boolean expression. Has a lower precedence than !

or
Boolean or with lower precedence than ||

redo
Restarts execution in the current block. 

rescue
Starts an exception section of code in a begin block. 

retry
Retries an exception block. 

return
Exits a method. If met in top-level scope, immediately stops interpretation of the current file.

self
The object the current method is attached to. 

super
Calls the current method in a superclass. 

then
Indicates the end of conditional blocks in control structures. 

true
Boolean true. 

undef
Prevents a class or module from responding to a method call. 

unless
Used for unless and modifier unless statements. 

until
Creates a loop that executes until the condition is true. 

when
A condition in a case expression. 

while
Creates a loop that executes while the condition is true. 

yield
Starts execution of the block sent to the current method.

Literals

https://docs.ruby-lang.org/en/master/syntax/literals_rdoc.html

A literal is any notation that lets you represent a fixed value in source code (wikipedia).

What does that even mean?

The notation is it's direct or literal value: e.g. 1 is a literal for the number 1, "hello" is a literal for the string "hello", [1, 2, 3] is a literal for the array [1, 2, 3]. It's a constant or fixed value, it doesn't change. (e.g. 1 is always 1, "hello" is always "hello", [1, 2, 3] is always [1, 2, 3]). It's the primary way to introduce values into a program.

This is different from the indirect or variable notation: x = 1 where x is a variable which refers to 1, which can change. Or for Constants FOO = 1, Expressions (1 + 2), or Methods def foo; puts 1 end which all have indirect or computed values.

Basic literals:

# Numbers: Integers and Floats
1, 2, 3.00, 4e2

# Strings
'That\'s right', "double quotes", "Interpolation like #{2+2}"

# Symbols (immutable strings)
:pending, :"rejected", :"#{var}_name"

# Arrays
[1, 2, 3, 4, 5]

# Hashes
{ key1: 'value1', key2: 'value2' }

# Ranges
1..100, 'a'..'z'

# Boolean: 
true, false

# Nil
nil

# Here Document or Heredoc
a_variable = <<HEREDOC
  This is a heredoc
  It is used for multi-line strings
HEREDOC

Regular Expression - Regex

A regular expression (also called a regex or regexp) is pattern that can be matched against a string. It is a way of specifying a set of characters that matches a string or part of a string. It is a match pattern (also simply called a pattern). Regex can be used for pattern matching and pattern replacement. Specific patterns can be defined with: Anchors, word boundaries, character classes, repetition, alternation and grouping.

re = /red/ # or %r{red}
re.match?('redirect') # => true   # Match at beginning of target.
re.match?('bored')    # => true   # Match at end of target.
re.match?('foo')      # => false  # No match.
"bored".match?(re)    # => true

The following are metacharacters with specific meaning: . ? - + * ^ \ | $ ( ) [ ] { }

https://docs.ruby-lang.org/en/master/Regexp.html#class-Regexp-label-Special+Characters

Operater =~ returns characters offset of beginning:

/cat/ =~ 'dog and cat' # => 8
/cat/ =~ 'cat' # => 0
'cat' =~ /cat/ # => 0

!~ is the negative match operator, which returns true if the string does not match the pattern:

/cat/ !~ 'dog and cat' # => false

Changing strings with patterns: .sub, .gsub, .sub!, and .gsub!. Sub is for the first match, gsub is for all matches.

Regex has modifiers, with the x at the last example below, you can add newlines, whitespace and comments inside to make it more readable:

/cat/i # => case insensitive
/cat/m # => multiline
/cat/s # => single line
%r{(\d{5}),         # 5 digits followed by comma
        \s,         # a whitespace
    ([A-Z])         # 1 character
  }x # => extended

After a succesful match via Regexp#match or =~ it returns a MatchData object, which is a collection of information about the match:
https://docs.ruby-lang.org/en/master/MatchData.html

/all/.match("all things")
=> #

Numbers

Ruby supports integers, floating-point, rational and complex numbers. Intergers are assumed to be decimal base 10, but can be specified with a leading sign, as base indicatar: 0 for octal, 0x for hexadecimal and 0b for binary (and 0d for decimal), followed by a string of digits in the appropriate base.

12345       => 12345  # base 10
0d12345     => 12345  # base 10
123_456     => 123456 # base 10
-543        => -543   # base 10
0xaabb      => 43707  # base 16 (hexadecimal)
0377        => 255    # base 8  (octal)
-0b10_1010  => -42    # base 2  (binary)
1_2_3       => 123    # base 10

BigDecimal is Ruby's high-precision decimal number class.

Rational numbers are the ratio of two integers (they are fractions) and therefor have an exact representation:

3/4             #=> 0
3/4r            #=> (3/4)
0.75r           #=> (3/4)
"3/4".to_r      #=> (3/4)
Rational(3,4)   #=> (3/4)
Rational("3/4") #=> (3/4)

Complex numbers represent points on the complex plane, and have 2 components: the real and imaginary parts.

1+2i            # => (1+2i)
"1+2i".to_c     # => (1+2i)
Complex(1,2)    # => (1+2i)
Complex("1+2i") # => (1+2i)

Looping using Numbers

3.times { print "A " }
1.upto(5) { |i| print i, " " }
99.downto(97) { |i| print i, " " }
50.step(60, 5) { |i| print i, " " }

# A A A 1 2 3 4 5 99 98 97 50 55 60

Strings

Ruby strings are sequences of characters and instances of class String.

Usually strings are created using string literals - sequences of characters between single or double quotes (delimiters). How the string literal is created, defines the amount of processing that is done on the characters in the string.

Escaping characters inside single-quote is a form of processing:

'hi \\' # => hi \
'that\'s right' # => that's right
'hi "\\"' # => hi "\"

Double-quoted strings support:

  • many escape sequences, e.g. \n the newline character.
  • string interpolation, which means you can use any ruby code into a string using #{ expression }.
  • global, class or instance variables: #$foo, #@@foo or #@foo.

Not recommended:

puts "now is #{ 
  def the(a)
    'the ' + a
  end
  the('time')
} for all bad coders..."

Produces: now is the time for all bad coders...

Some style guides prefer single quotes, if interpolation isn't used, because they are faster.

Syntax to create a string literal can also be as follows, with any nonalphanumeric or nonmultibyte character:

%q/abc/         #=> abc
%Q!abc!         #=> abc
%Q{abc #{2*3}}  #=> abc 6
%!abc!          #=> abc
%{abc #{2*3}}   #=> abc 6
# usually current style guides suggest this:
%q(abc)         #=> abc

Finally, you can construct a string using a here document, or heredoc.

string = <<END_OF_STRING
  This is a string
  with two lines.
END_OF_STRING

# with a minus sign, you can indent the terminator
string = <<-END_OF_STRING
This is a string
with two lines.
  END_OF_STRING

# with tilde, ruby will strip the indentation, to enable long strings
string = <<~END_OF_STRING
  This is a string
  with two lines.
END_OF_STRING

# Or generally considered super confusing:
print <<-S1, <<-S2
  Concat
    S1
  enate
  S2

Type conversion:

# to string
1.to_s 
# to integer
'1'.to_i

Encoding

Encoding is a mechanism for translating bits into characters. For many years, most developers who used English used ASCII, a 7-bit encoding of English characters, such as binary 101 to capital A. Later, an 8-bit representation called Latin-1 that included most characters in European languages became common. All of these were superseded by Unicode, a global standard for all text characters used in all languages: https://home.unicode.org/

Struct

A Struct is a class that is used to create objects that have attributes.

Ranges

Ranges represent a range of values. Ruby uses ranges to implement sequences and intervals.

arr = [1,2,3,4,5]
arr[..2] # => [1,2,3]
arr[2..] # => [3,4,5]
arr === 3 # => true
arr === 6 # => false
arr.include?(3) # => true

Blocks

A code block is a chunk of code that can be passed to a method. You can think of a block as somewhat like the body of an anonymous method, as if the block were another parameter passed to that method. Usually between braces on one line and do/end when block spans multiple lines. Parameters to a block are separated by commas, and they are always local to the block. You can define block-local variables using the ; character in the block's parameter list.

# general syntax
[1,2].each { puts 'x' } 
[1,2].each do puts 'x' end
[1,2].each { |x| puts x }
[1,2].each { puts _1 } # _1 first positional argument, _2, _3 etc.

# block-local variable y (syntax is rare)
y = 100
sum = 0
[1,2].each do |x; y| 
  y = x*x
  sum += y
end
puts sum
puts y

# method say first, then parameters, only one block after.
object.say("dave") { puts 'hello' }

The act of doing something to all objects in a collection is called enumeration in Ruby; in other languages it is called iteration. e.g. each, find, map, sort_by, group_by, map, reduce.

Ruby remembers the context of an object, local variables, block, and so on, this is called binding. Within the method, the block may be invoked, using the yield statement. A block returns a value to the method that yields to it. The value of the last expressions evaluated in the block is passed back to the method as the value of the yield expression.

def two_times 
  yield
  yield
end
two_times { puts "Hello" }
Hello 
Hello

`.tap` # is a no-op, it taps into the object and returns the object. It is useful for debugging e.g.: 
`.tap { |result| puts "result: #{result}\n\n" }`

# map implementation looks something like this, which constructs a new array
class Array
  def map
    result = []
    each do |value| 
      result << yield(value) 
    end
    result 
  end
end

If the last parameter is prefixed by & (such as &action), that code block is converted to an object of class Proc. The Proc object is then assigned to the parameter. This allows you to pass a code block to a method as if it were a regular parameter.

# Long
class ProcObject
  def pass_in(&action)
    @stored_proc = action
  end

  def use_proc(parameter)
    @stored_proc.call(parameter)
  end
end
foo = ProcObject.new
foo.pass_in{ |paramz| puts "Hello, #{paramz}!" }
foo.use_proc("Dave")
# => Hello, Dave!

# shorter
def create_block_object(&block)
  block
end
bl = create_block_object { |x| puts "Hello, #{x}!" }
bl.call('Dave')
# => Hello, Dave!

# shortest
# stabby lambda
bl = -> (param) { puts "you called with #{param}" }
bl.call("Dave")
# => Hello, Dave!

# short: lambda (Ruby Kernal method)
bl = lambda { |param| puts "you called with #{param}" }
bl.call("Dave")
# => Hello, Dave!

# short: Kernal method proc
bl = proc { |param| puts "you called with #{param}" }
bl.call("Dave")
# => Hello, Dave!

# Proc.new (not the preferred method)
bl = Proc.new { |param| puts "you called with #{param}" }
bl.call("Dave")
# => Hello, Dave!

Blocks as closures

Variables in the surrounding scope that are referenced in a block remain accessible for the life of that block and the life of any Proc object created from that block. This is called a closure. More on closures: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

def n_times(thing)
  ->(n) { puts thing * n }
end
p1 = n_times("Hello,")
p1.call(3)
# => Hello,Hello,Hello,

# Parameter list
# it can take default values, splat arguments, keywords arguments, and block parameters.
-> (parameter list) { block }

# Example
proc2 = -> (x, *y, &z) do 
  puts x
  puts y
  z.call
end

proc2.call(1, 2, 3, 4) { puts "Hello, World!" }
# => 1
# => [2, 3, 4]
# => Hello, World!

Enumerators

Can iterate over two collections in parallel. Enumerator class is not to be confused with the Enumerable module. The Enumerator class is used to create custom external iterator.

short_enum = [1,2,3].to_enum
long_enum = ('a'..'z').to_enum
loop do 
  # will end after the 3rd iteration, this will terminate cleanly
  puts "#{short_enum.next} - #{long_enum.next}"
end

Control Flow and Expressions

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

Ruby has a variety of ways to control execution. All the expressions described here return a value.

Expressions and Return Values:

# Expression is evaluated
1 + 1
# and will return a value
1 + 1 
=> 2
# => is called a hash rocket
# 2 is returned

puts 1+1
2
=> nil
# expression puts 1+1 is evaluated
# 2 is printed
# nil is returned
  • Basic Operator Expressions: + - * / % **
  • Command Expressions: string with backquotes or backticks will be executed as command by OS. ls.split will give array of content of the current folder. Copying , using `echo 'hi' | pbcopy` will copy the output of echo to clipboard, which is the same as system("echo '123' | pbcopy"). Copying resource attributes: `echo "#{User.first.id}" | pbcopy`.
  • Assignment is setting the lvalue (left value) to refer to the rvalue (right value), and returns rvalue. Ruby has 2 forms of assignment: first an object reference to a variable or constant, ABC = 4. Second is object attribute or element reference on the left side of the assignment operator, ABC[1] = 4. Also possible, is the rightward assignment, since ruby 3.0: data => variable (e.g. 2=>x).
  • For parallel assignment, to swap vales:
a, b = 1, 2 
a, b = b, a
  • Splats and assignment:
    • for rvalues a,b,c,d,e = *(1..2), 3 # a=1, b=2, c=3, d=nil, e=nil
    • greedy for splat for lvalue:
a, *b = 1,2,3,4,5 
# a=1, b=[2,3,4,5]

*a, b = 1,2,3,4,5 
# a=[1,2,3,4], b=5

first, *, last = [1,2,3,4,5] 
# first=1, last=5
  • Nested assignments:
a, (b, c) = 1, [2, 3] 
# a=1, b=2, c=3

a, (b, c), d = 1, [2, 3, 4], 5 
# a=1, b=2, c=3, d=5

Conditional Execution

  • boolean expressions: Ruby has simple definition of truth: any value that is 1. not nil, or 2. the constant false, is true. So, "c", 9, 0, :a, are true, also, "", [], {} are true. The set of false values are sometimes referred to as falsey and set of true values are referred to as truthy. nil && 99 returns nil, "c" && 99 returns 99. When it's false, the first argument is returned, when it's truee, the second argument is returned (short circuit evaluation). There is a difference in using && and and, terms of precedence compared to the assignment. Examples: result = "" && [], which returns the #=> [], and will show result #=> [], however result = "" and [] which returns the #=> [] and will show result # => "".
  • the defined? Keyword: defined? 1 #=> "expression" and defined? a #=> nil and defined? a = 1 #=> "assignment".
  • Comparing objects: == equal value, ===, <=>, <, >, <=, >=, =~, eql? (equal type and value), equal? (same object id).
  • if and unless:
# then is optional
if condition then 
  # code
elsif condition2 then
  # code
else
  # code
end

# also possible
if condition then #code
elsif condition2 then #code
else #code
end

# assignment
variable = 
  if condition then #code
  elsif condition2 then #code
  else #code
  end

# ternary operator
condition ? true_value : false_value

# unless. Negated if statement. As reminder: if not
unless expression
  # some code to be executed if the expression is FALSE
else
  # some code to be executed if the expression is true
end

# also possible
if not false then true end #=> true

Safe navigation operator: &., also called the lonely operator. It returns nil if the object is nil.

Loops and iterators:

https://docs.ruby-lang.org/en/master/Kernel.html#method-i-loop

# a method defined in Kernel, but it looks like a control structure.
# loop
# e.g. iteration over api endpoint that is paginated
page = 1
collection = []
loop do
  # response = send_request(:get, '/endpoint', page)
  # collection += response[:data]
  # page += 1
  # break if page > response.dig(:pagination, :total_pages)
end

# while (do keyword is optional)
a = 0
while a < 10 do
  p a
  a += 1
end
p a

# until. As reminder: while not
until condition
  # loop as long as condition is false
end

Break, next:

  • Use break to leave a block early.
  • Use next to skip the rest of the current iteration.
loop do
  next if condition_1
  break if condition_2
end

Iterators:

2.times do; puts 'Hello' end # => Hello Hello
2.times { puts 'Hello' } # => Hello Hello
0.upto(5) { |i| puts i } # => 0 1 2 3 4 5
0.step(10, 2) { |i| puts i } # => 0 2 4 6 8 10

A different way to write an each loop with a Ruby built-in looping primitive:

for i in 0..5
  puts i
end
# => 0 1 2 3 4 5

Block local variables:

square = 'start'
[1,2].each do |i; square| # square is now also a block local variable
  square = i * i
end
puts square # => start

Pattern Matching

https://docs.ruby-lang.org/en/master/doc/syntax/pattern_matching_rdoc.html

Pattern matching compares a target which can be any Ruby object to a pattern. If the target matches the pattern, the target is deconstructed into the pattern, setting the value of those variables.

"abc" in "abc" # => true
"abc" in "def" # => false
3 in 3 # => true
3 in 4 # => false
3 in 1..5 # => true
"abc" in String # => true
"abc" in Integer # => false
[1,2,3] in [Integer, Integer, Integer] # => true
{a: 1, b: "3"} in {a: Integer, b: String } # => true

# or
[1,2] in [Integer, Integer] | [Integer, String] # => true

Variable binding: Assign values in the target to variables in the pattern and then use those variables in the pattern.

value in pattern => variable
puts variable # => value

# example
"aaa" in String => var
puts var # => aaa

# short
"baa" => var
puts var # => baa

# another way
"abc" in var
puts var # => abc

Case pattern matching:

# case, with when clause
case expression
when condition1 then # code
when condition2
  # code
else # code
end

# case, with in clause
case expression
in condition1 then # code
in condition2 then # code
else # code
end

# pinning values, in a case statemen. With the pin operator ^ : It will pin the value to the part of the pattern..
def get_status(idea_to_look_for, status_to_look_for, list)
  case list
  in [*, {idea: ^idea_to_look_for, status: }, *] then puts "#{idea_to_look_for} is #{status_to_look_for}"
  in [*, {idea:, status: ^status_to_look_for}, *] then puts "second"
  else # code
end

puts get_status('idea1', 'status1', 
  [{idea: 'idea1', status: 'status1'}, 
  {idea: 'idea2', status: 'status2'}
])
#=> idea1 is status1

# guard clause, additionally to the pattern matching, it checks the condition
case expression
in pattern if condition # code
else # code
end

# pattern matching against a class, requires a deconstruct_keys or deconstruct
class MyClass
  attr_accessor :name

  def initialize(name)
    @name = name
  end

  def deconstruct_keys(keys)
    {name: @name}
  end
end

my_object = MyClass.new('my_object')

case my_object
in {name: /^my/} then puts "starts with my"
in {name: /^your/} then puts "starts with your"
else # code
end

Variables

https://docs.ruby-lang.org/en/master/doc/syntax/assignment_rdoc.html

A variable is an identifier that is assigned to an object, and which may hold a value. A variable is not an object in Ruby, so it is a reference to an object. A local variable name may contain letters, numbers, an _ (underscore or low line) or a character with the eighth bit set.

Assignment aliases objects, potentially giving multiple variables that reference the same object. String#dup will create a new string object with the same content. String#freeze will make a string immutable. Numbers and symbols are always frozen (immutable) in Ruby.

Examples:

$global_variable
@@class_variable
@instance_variable
CONSTANT
::TOP_LEVEL_CONSTANT
OtherClass::CONSTANT
local_variable

In a file:

# Defining variables
# global variable, can be mutated
$some_global_variable = "accessible everywhere"
# Top level constant, can not be mutated
TOP_LEVEL_CONSTANT = "accessible everywhere"

class Toy
  CONSTANT = "Some value"
end

class Human
  # A class variable is shared by all instances of this class.
  @@species = 'H. sapiens'
  # Constant is a variable that is set only once and never changed.
  A_CONSTANT = 1

  # Basic initializer
  def initialize(name, age = 0)
    # Assign the argument (name) to the '@name' instance variable for the instance.
    @name = name
    # If no age given, we will fall back to the default in the arguments list (age=0).
    @age = age
  end

  def some_method
    # local_variable is only accessible within this method
    local_variable = 1 
  end

  def some_other_method
    # TOP_LEVEL_CONSTANT can be accessed from anywhere in the program using :: prefix
    ::TOP_LEVEL_CONSTANT
  end

  def some_last_method
    # From another class
    Toy::CONSTANT
  end
end

# Showing variables
p $some_global_variable
Human.class_variable_get(:@@species)
Human::A_CONSTANT
h = Human.new('foo', 10)
h.instance_variable_get(:@name)
h.instance_variable_get(:@age)
h.some_method
h.some_other_method
h.some_last_method
# Aliasing global variables
$some_global_variable = "accessible everywhere"
alias $b $some_global_variable
p $b # => 'accessible everywhere'

# parallel variable assignment
x, y, z = 100, 200, 500

Pseudo Variables

They provide information about the program's execution environment or serve specific purposes within Ruby.
Characteristics: Predefined, read-only and available throughout the program.

self  # The receiver object of the current method.
super # The receiver object of the current method in the superclass.
true  # boolean; singleton; TrueClass
false # boolean; singleton; FalseClass
nil   # empty; uninitialized; NilClass; falsey; singleton
__FILE__ # The name of the current source file.
__LINE__ # The current line number in the source file.

Pre-defined global variables

In irb:

global_variables.count
# => 43
global_variables.sort.inspect
# => "[:$!, :$\", :$$, :$&, :$', :$*, :$+, :$,, :$-0, :$-F, :$-I, :$-W, :$-a, :$-d, :$-i, :$-l, :$-p, :$-v, :$-w, :$., :$/, :$0, :$:, :$;, :$<, :$=, :$>, :$?, :$@, :$DEBUG, :$DEBUG_RDOC, :$FILENAME, :$LOADED_FEATURES, :$LOAD_PATH, :$PROGRAM_NAME, :$VERBOSE, :$\\, :$_, :$`, :$stderr, :$stdin, :$stdout, :$~]"

Exceptions 
  $! (Exception)
  $@ (Backtrace)

Pattern Matching 
  $~ (MatchData)
  $& (Matched Substring)
  $` (Pre-Match Substring)
  $' (Post-Match Substring)
  $+ (Last Matched Group)
  $1, $2, Etc. (Matched Group)

Separators 
  $/ (Input Record Separator)
  $; (Input Field Separator)
  $\ (Output Record Separator)

Streams 
  $stdin (Standard Input)
  $stdout (Standard Output)
  $stderr (Standard Error)
  $< (ARGF or $stdin)
  $> (Default Standard Output)
  $. (Input Position)
  $_ (Last Read Line)

Processes 
  $0
  $* (ARGV)
  $$ (Process ID)
  $? (Child Status)
  $LOAD_PATH (Load Path)
  $LOADED_FEATURES

Debugging 
  $FILENAME
  $DEBUG
  $VERBOSE

Other Variables 
  $-a
  $-i
  $-l
  $-p

Deprecated 
  $=
  $,

Pre-defined global constants

Streams 
  STDIN
  STDOUT
  STDERR

Environment 
  ENV
  ARGF
  ARGV
  TOPLEVEL_BINDING
  RUBY_VERSION
  RUBY_RELEASE_DATE
  RUBY_PLATFORM
  RUBY_PATCHLEVEL
  RUBY_REVISION
  RUBY_COPYRIGHT
  RUBY_ENGINE
  RUBY_ENGINE_VERSION
  RUBY_DESCRIPTION

Embedded Data 
  DATA

Methods

https://docs.ruby-lang.org/en/master/syntax/methods_rdoc.html

Defined by keyword def. You can undefine by undef.

Can begin with lowercase or underscore, followed by letters, numbers or underscores. May end with ?, !, =.

  • Predicate methods end with a ? and return a boolean result.
  • Bang methods end with a ! and modify the object in some way. E.g. String .reverse or .reverse!. The first one returns a modified string and the second one modifies the receiver in place.
  • Assignment methods end with = and modify the object in some way.

Parentheses are optional: def hello; end is the same as def hello() end.

A method is invoked using dot syntax: receiver.method

In other words:

  • We ask the object to perform an action.
  • The object receives a message.
  • We send a message to the object.

Preference to use parentheses in all but the simplest cases. This would be idiomatic, it means in line with the language's conventions.

def hello
  puts 'hi'
end

Since Ruby 3.0 endless method:
def a_method(arg) = puts arg

Method arguments

def hello(greeting = "hi", name = "bob", question, *args)
  puts "#{greeting} #{name} #{question} #{args}"
end

- `greeting` is a default argument.
- `name` is a default argument.
- `question` is a required argument.
- `args` is a splat argument. It collects all remaining arguments into an array.

A class method: def self.method_name and an instance method: def method_name.

Positional arguments: are passed to the method based on their position.
Keyword arguments: are passed based on the keyword and can be listed in any order.
Keyword arguments: def method_name(city: "value", state:) When calling the method, you can pass the arguments in any order, but each keyword argument must be part of the call: method_name(state: "CA", city: "San Francisco").

Collect arguments into Hash with double-splat, or : `def method_name(args)`. A bare single splat will catch positional arguments, bare double splat will catch keyword arguments.

def do_stuff(*)
  # anonymous splat parameter 
  do_stuff_2(*)
end

def do_stuff_2(*array_args)
  array_args
end

def do_stuff_3(first, *, last)
  puts "first: #{first}, last: #{last}"
end

# passing bare & character to pass block
class Child < Parent
  def do_it(&)
    do_it_2(&)
  end
end

# will catch all arguments.
def do_it(*args, **kwargs, &block); end 

# the triple dot syntax will catch and pass all arguments, in a simpler anonymous way.
def do_it(...)
  do_it_2(...)
end

Calling a method.

connection.download_mp3("jazz", speed: :slow) { |p| show_progress(p) }
# receiver.method(postional_parameter, keyword_parameter: "value") { |block_parameter| block_code(block_parameter) }
# 1. object invokes method
# 2. inside that method, self is set to that (receiver) object
# 3. method body is executed, possibly the block is called as well

# Ruby allows you to omit the receiver, in which case Ruby will default to use `self`.
class Thing
  def hello
    self.greet
    greet
  end

  private def greet
    puts 'hi'
  end
end
Thing.new.hello
# "hi"
# "hi"
# => nil

Method calls without parentheses are sometimes called commands.

rule: If in doubt, use parentheses.

A return statement exists from the currently executing method. It can be used to return a value from a method. If no value is specified, nil is returned.

def method_name(city:, country:)
  puts city
  puts country
end
data = {city: "ab", country: "bb"}
method_name(**data)
#ok

city = "cc"
country = "aac"
method_name(city:, country:)
#ok

# Passing block arguments:
["a", "f"].map(&:upcase)
# take the argument to this proc, and call the method whose name matches this symbol.
# the class Symbol implements the to_proc method, returning a Proc method.


# https://ruby-doc.org/3.3.4/Object.html#method-i-method
# objects have a method named method, which takes a symbol and returns the object's method of the same name
number = 2
method = number.method(:*)
(1..3).map(&method)
# => [2, 4, 6]

Classes

In object oriented programming, a class is a blueprint for a domain concept.

Instances are created by a constructor. The standard constructor method is called new. When you call Bike.new, Ruby holds an uninitialized object and calls that objects initialize method, passing all arguments from .new. This sets up the object's state. Instances have a unique object_id (object identifier).

# is the default string representation of an object.

class Bike
  def initialize(price)
    @price = price
  end
end
bike = Bike.new(100)
puts bike 
#