- Basic Syntax
- Operators
- Declarations
- Functions
- Built-in Types
- Type Conversions
- Modules
- Control Structures
- Arrays, Hashes, and Ranges
- Classes
- Blocks and Yield
- Symbols
- Mixins
- Exceptions
- Threads
- Snippets
- TODO Http-Server
- Imperative, Object-Oriented language
- Dynamically typed
- Syntax similar to Python/Perl, with extensive use of blocks and procs
- Everything is an object, including numbers and classes
- Rich metaprogramming features
- Uses modules to share functionality across classes
- Blocks, Procs, and Lambdas for functional programming
- Exception handling with
begin-rescue
- Built-in threading with native OS threads
File hello.rb
:
Ruby has a set of reserved keywords that cannot be used as variable names, method names, or any identifiers. These words have specific meanings in the language and are used for control structures, declarations, and other language constructs.
Here’s a list of Ruby's reserved keywords:
__ENCODING__ __LINE__ __FILE__
BEGIN END alias
and begin break
case class def
defined? do else
elsif end ensure
false for if
in module next
nil not or
redo rescue retry
return self super
then true undef
unless until when
while yield
-
__ENCODING__
,__LINE__
, and__FILE__
are special constants that return the encoding of the script, the current line number, and the name of the file, respectively. -
BEGIN
andEND
are used to define code blocks that run before and after the program's execution. -
and
, **or**
, andnot
are logical operators but behave differently in terms of precedence compared to&&
,||
, and!
.
These keywords are reserved by the language and should be avoided as custom identifiers in your code.
puts "Hello Ruby"
$ ruby hello.rb
Operator | Description |
---|---|
+ |
addition |
- |
subtraction |
* |
multiplication |
/ |
quotient |
% |
remainder |
& |
bitwise and |
| |
bitwise or |
^ |
bitwise xor |
<< |
left shift |
>> |
right shift |
Operator | Description |
---|---|
== |
equal |
!= |
not equal |
< |
less than |
<= |
less than or equal |
> |
greater than |
>= |
greater than or equal |
Operator | Description |
---|---|
&& |
logical and |
|| |
logical or |
! |
logical not |
Operator | Description |
---|---|
& |
address of / create reference |
* |
dereference reference |
# Variable declaration and assignment
foo = 42 # no need to declare type, dynamically assigned
foo, bar = 42, 1302 # multiple assignment
# Constants (by convention, constants are written in uppercase)
CONSTANT = "This is a constant"
# Constants are defined with an uppercase name, and are meant to remain unchanged
MAX_SIZE = 100
# Local variables start with a lowercase letter or an underscore
local_variable = "I'm local"
# Global variables start with a dollar sign ($)
$global_variable = "I'm global"
# Instance variables start with an '@' sign and are used within classes
@instance_variable = "I'm an instance variable"
# Class variables start with two '@' signs and are shared among all instances of the class
@@class_variable = "I'm a class variable"
# Symbols are immutable, reusable constant identifiers, often used as keys or identifiers
:my_symbol
Ruby automatically infers the type based on the assigned value. No need for explicit type declaration.
# a simple function
def function_name
end
# function with parameters
def function_name(param1, param2)
end
# function with a return type
def function_name
return 42
end
# return multiple values
def return_multi
return 42, "foobar"
end
x, str = return_multi
# use default values for parameters
def function_name(param1 = 42, param2 = "foo")
end
# return named results
def return_multi2
n = 42
s = "foobar"
return n, s
end
x, str = return_multi2
# Blocks
def method_with_block
yield if block_given?
end
method_with_block { puts "Block executed!" }
# Procs
add = Proc.new { |a, b| a + b }
puts add.call(3, 4) # Outputs 7
# Lambdas
multiply = ->(a, b) { a * b }
puts multiply.call(3, 4) # Outputs 12
# Ruby supports variadic functions using the splat operator (*)
def adder(*args)
args.sum
end
puts adder(1, 2, 3) # Outputs 6
puts adder(9, 9) # Outputs 18
nums = [10, 20, 30]
puts adder(*nums) # Outputs 60
# Numeric types
Integer # Fixnum and Bignum in older versions of Ruby
Float # floating-point numbers
Complex # complex numbers
# Boolean types
TrueClass
FalseClass
# Strings
String
# Symbols
Symbol
# Arrays and Hashes
Array
Hash
# Nil and Other Basic Types
NilClass
Range
## Type Conversions
In Ruby, explicit type conversions are done using methods like `to_i`, `to_f`, `to_s`, etc.
```ruby
# Converting types
i = "42".to_i # string to integer
f = "42.42".to_f # string to float
s = 42.to_s # integer to string
b = 1.to_s == "1" # integer to boolean-like comparison
.to_i
- Converts to integer..to_f
- Converts to float..to_s
- Converts to string..to_sym
- Converts to symbol.
Modules are collections of methods and constants. They can be used as namespaces or mix-ins (to share functionality across classes).
# Defining a module
module MyModule
MY_CONSTANT = 100
def my_method
puts "Hello from MyModule"
end
end
# Including a module in a class
class MyClass
include MyModule
end
obj = MyClass.new
obj.my_method # => "Hello from MyModule"
- Modules are not instantiable.
- They can be mixed into classes via
include
(for instance methods) orextend
(for class methods).
Ruby uses if
, elsif
, and else
for conditional control.
x = 10
if x > 0
puts "x is positive"
elsif x < 0
puts "x is negative"
else
puts "x is zero"
end
# Single-line if modifier
puts "x is positive" if x > 0
Ruby supports several ways to loop, including while
, until
, for
, and iterators like each
.
# while loop
i = 0
while i < 5 do
puts i
i += 1
end
# until loop
i = 5
until i == 0 do
puts i
i -= 1
end
# for loop
for i in 1..5 do
puts i
end
# Using an iterator
(1..5).each do |i|
puts i
end
The case
statement in Ruby works like a switch in other languages.
fruit = "apple"
case fruit
when "apple"
puts "This is an apple"
when "banana"
puts "This is a banana"
else
puts "Unknown fruit"
end
# You can also use ranges and regular expressions in case
age = 25
case age
when 0..17
puts "You're a minor"
when 18..64
puts "You're an adult"
else
puts "You're a senior"
end
- The
case
statement compares the object against eachwhen
condition, executing the first matching one.
## Arrays, Hashes, and Ranges
### Arrays
An array in Ruby is an ordered collection of elements, which can be of any type. Arrays are indexed starting from 0.
```ruby
arr = [1, 2, 3, "four", 5.0]
puts arr[0] # => 1
puts arr[-1] # => 5.0 (last element)
puts arr[1..3] # => [2, 3, "four"] (range of elements)
- Arrays are dynamic, meaning you can add or remove elements easily.
- Use
push
,<<
to append andpop
to remove from the end. - Use
shift
,unshift
to remove or add from the start.
arr.push(6) # => [1, 2, 3, "four", 5.0, 6]
arr << 7 # => [1, 2, 3, "four", 5.0, 6, 7]
arr.pop # => 7 (removes last element)
arr.shift # => 1 (removes first element)
arr.unshift(0) # => [0, 2, 3, "four", 5.0, 6]
A hash in Ruby is a collection of key-value pairs, where keys can be symbols, strings, or other types.
h = {name: "Alice", age: 25, city: "Paris"}
puts h[:name] # => "Alice"
puts h[:age] # => 25
h[:country] = "France"
puts h # => {:name=>"Alice", :age=>25, :city=>"Paris", :country=>"France"}
- Hashes are unordered, but in Ruby 1.9+, they maintain insertion order.
- You can use
each
to iterate over key-value pairs.
h.each do |key, value|
puts "#{key}: #{value}"
end
Ruby provides a variety of methods to manipulate arrays.
arr = [1, 2, 3, 4, 5]
# Basic operations
arr.reverse # => [5, 4, 3, 2, 1]
arr.sort # => [1, 2, 3, 4, 5]
arr.uniq # => [1, 2, 3, 4, 5] (removes duplicates)
arr.include?(3) # => true
# Array transformations
arr.map { |x| x * 2 } # => [2, 4, 6, 8, 10]
arr.select { |x| x.even? } # => [2, 4]
arr.reject { |x| x.even? } # => [1, 3, 5]
A range is a sequence of values, often used to generate numbers or work with loops.
r = 1..5 # includes 1, 2, 3, 4, 5
r = 1...5 # includes 1, 2, 3, 4 (excludes 5)
# Convert range to array
arr = (1..5).to_a # => [1, 2, 3, 4, 5]
Ranges are useful in loops, conditionals, and array slicing.
(1..3).each { |i| puts i } # => prints 1, 2, 3
Ruby is a pure object-oriented language, meaning every value is an object. Classes define the blueprint for objects.
class Person
attr_accessor :name, :age # defines getter and setter methods
def initialize(name, age)
@name = name # instance variables
@age = age
end
def introduce
puts "Hi, I'm #{@name} and I'm #{@age} years old."
end
end
# Creating an object
person = Person.new("Alice", 25)
person.introduce # => "Hi, I'm Alice and I'm 25 years old."
- Instance Variables (
@var
) are used to store object-specific data. - Class Variables (
@@var
) are shared across all objects of a class. - Class Methods are defined using
self.method_name
ordef self.method_name
.
Ruby supports inheritance, where one class can inherit methods and attributes from another.
class Animal
def speak
puts "Generic animal sound"
end
end
class Dog < Animal
def speak
puts "Woof!"
end
end
dog = Dog.new
dog.speak # => "Woof!"
Blocks in Ruby are chunks of code enclosed in {}
or do..end
that can be passed to methods.
def my_method
puts "Start of method"
yield # Executes the block passed to the method
puts "End of method"
end
my_method { puts "Hello from the block!" }
# Output:
# Start of method
# Hello from the block!
# End of method
- Yield is used to call the block inside a method.
- Blocks can accept arguments.
def my_method
yield(5) # Passes 5 to the block
end
my_method { |num| puts num * 2 } # => 10
Blocks can also be stored in variables as Procs or Lambdas and passed around.
my_proc = Proc.new { |x| puts x * 2 }
my_proc.call(10) # => 20
my_lambda = ->(x) { puts x * 3 }
my_lambda.call(10) # => 30
## Symbols
A symbol in Ruby is a lightweight, immutable identifier often used in place of strings for labels, keys, or identifiers. Symbols are denoted by a colon (`:`) followed by the name of the symbol.
```ruby
:name
:age
:city
-
Immutable: Symbols cannot be changed once created, whereas strings are mutable.
-
Memory-efficient: Symbols are only stored once in memory, making them more efficient for repeated use.
-
Symbols are often used as keys in hashes, method names, and references to methods.
person = {name: "Alice", age: 25, city: "Paris"}
puts person[:name] # => "Alice"
Symbols are particularly useful when the identifier doesn’t need to change or when comparisons need to be fast.
puts :name == :name # => true
Ruby uses mixins to allow modules to share functionality across multiple classes without inheritance. This is accomplished by including or extending Modules.
Modules are like classes but cannot be instantiated. They are used for grouping methods, constants, and other functionality that can be reused across classes.
module Walkable
def walk
puts "I'm walking!"
end
end
class Person
include Walkable # Mixin Walkable module
end
person = Person.new
person.walk # => "I'm walking!"
-
include
mixes in module methods as instance methods. -
extend
mixes in module methods as class methods.
module Greetable
def greet
puts "Hello!"
end
end
class Animal
extend Greetable
end
Animal.greet # => "Hello!" (Class method)
Ruby provides a robust exception-handling mechanism using begin
, rescue
, ensure
, and raise
. Exceptions allow you to manage errors in your program gracefully.
begin
# Code that might raise an exception
result = 10 / 0
rescue ZeroDivisionError
puts "Can't divide by zero!"
ensure
puts "This will always run"
end
rescue
is used to capture and handle exceptions.ensure
runs no matter what (whether an exception occurs or not).raise
is used to manually throw an exception.
def divide(a, b)
raise ArgumentError, "Division by zero is not allowed" if b == 0
a / b
end
begin
divide(10, 0)
rescue ArgumentError => e
puts e.message
end
You can define your own exception classes by inheriting from StandardError
.
class CustomError < StandardError; end
begin
raise CustomError, "Something went wrong!"
rescue CustomError => e
puts e.message # => "Something went wrong!"
end
Ruby provides native support for multithreading. Threads allow you to execute code concurrently.
thread = Thread.new do
puts "Thread is running"
end
thread.join # Waits for the thread to finish
Thread.new
creates a new thread.join
makes the main program wait for the thread to finish.
thread = Thread.new(10, 20) do |a, b|
puts a + b # => 30
end
thread.join
Threads may access shared resources, leading to potential conflicts. Ruby provides synchronization mechanisms to avoid such issues, such as Mutex (mutual exclusion).
mutex = Mutex.new
counter = 0
threads = []
5.times do
threads << Thread.new do
mutex.synchronize do
temp = counter
sleep(0.1)
counter = temp + 1
end
end
end
threads.each(&:join)
puts counter # => 5
- Mutex ensures that only one thread can execute a piece of code at a time, preventing race conditions.
## Snippets
This section contains useful Ruby code snippets that can be used in everyday programming.
### 1. Swap Two Variables
```ruby
a = 10
b = 20
a, b = b, a
puts a # => 20
puts b # => 10
string = "Hello, world!"
substring = "world"
puts string.include?(substring) # => true
str = "hello"
sym = str.to_sym
puts sym # => :hello
sym = :ruby
str = sym.to_s
puts str # => "ruby"
nested_array = [1, [2, 3], [4, [5, 6]]]
flat_array = nested_array.flatten
puts flat_array # => [1, 2, 3, 4, 5, 6]
array = [1, 2, 2, 3, 3, 3]
unique_elements = array.uniq
puts unique_elements # => [1, 2, 3]
people = [{name: "Alice", age: 25}, {name: "Bob", age: 20}, {name: "Charlie", age: 30}]
sorted_people = people.sort_by { |person| person[:age] }
puts sorted_people
# => [{:name=>"Bob", :age=>20}, {:name=>"Alice", :age=>25}, {:name=>"Charlie", :age=>30}]
array = [10, 5, 20, 8]
largest = array.max
puts largest # => 20
person = {name: "Alice", age: 25, city: "Paris"}
person.each do |key, value|
puts "#{key}: #{value}"
end
# Output:
# name: Alice
# age: 25
# city: Paris
- Reading a File
File.open("example.txt", "r") do |file|
puts file.read
end
- Writing to a File
File.open("example.txt", "w") do |file|
file.write("Hello, file!")
end
require 'date'
current_time = Time.now
puts current_time # => Current date and time
current_date = Date.today
puts current_date # => Current date
range = (1..5).to_a
puts range # => [1, 2, 3, 4, 5]
array = [1, 2, 3, 4, 5]
even_numbers = array.select { |num| num.even? }
puts even_numbers # => [2, 4]
name = "Alice"
greeting = "Hello, #{name}!"
puts greeting # => "Hello, Alice!"
greet = ->(name) { puts "Hello, #{name}!" }
greet.call("Bob") # => "Hello, Bob!"