Skip to content

Latest commit

 

History

History
1070 lines (858 loc) · 20.4 KB

File metadata and controls

1070 lines (858 loc) · 20.4 KB

RUBY PROGRAMMING COMPREHENSIVE CHEATSHEET

Comprehensive Ruby cheatsheet covering syntax, data types, OOP, modules, blocks, exceptions, file I/O, regex, Enumerable, and common gems. Includes naming conventions, operators, loops, hashes, arrays, and practical code examples—perfect for beginners and experienced devs needing quick reference.

Table of Contents

  1. Basic Syntax & Execution
  2. Data Types
  3. Variables & Constants
  4. Operators
  5. Conditionals
  6. Loops & Iterators
  7. Arrays
  8. Hashes
  9. Strings
  10. Symbols
  11. Methods (Functions)
  12. Classes & Objects (OOP)
  13. Modules & Mixins
  14. Blocks, Procs & Lambdas
  15. Exception Handling
  16. File I/O
  17. Regular Expressions
  18. Enumerable Module
  19. Common Gems
  20. Ruby Style Guide

1. Basic Syntax & Execution

# Single line comment

=begin
Multi-line comment
=end

# Print with newline
puts "Hello World"

# Print without newline
print "Hello"

# Print with inspect (useful for debugging)
p "Hello"  # => "Hello"

# Get user input
name = gets.chomp

# Execute: ruby filename.rb
# Interactive: irb or pry

2. Data Types

# Numbers
integer = 42
float = 3.14
big_num = 1_000_000_000  # underscores for readability

# Boolean
true_obj = true
false_obj = false
nil_obj = nil  # represents "nothing" (falsey)

# Strings
str = "double quotes - interpolation: #{2 + 2}"
str2 = 'single quotes - literal: #{2 + 2}'

# Symbols (immutable, memory efficient)
symbol = :name
:hello == "hello".to_sym

# Arrays
arr = [1, 2, 3, "four", :five]

# Hashes
hash = { name: "Alice", age: 30 }  # Ruby 1.9+ syntax
old_hash = { :name => "Bob", :age => 25 }

# Ranges
range = 1..10   # inclusive
range2 = 1...10  # exclusive (10 not included)

3. Variables & Constants

# Variable naming conventions
local_var = "lowercase with underscores"     # local variable
@instance_var = "starts with @"              # instance variable
@@class_var = "starts with @@"               # class variable
$global_var = "starts with $"                # global variable
CONSTANT = "ALL_CAPS"                        # constant (warning if reassigned)

# Parallel assignment
a, b, c = 1, 2, 3
x, y = [10, 20]  # x=10, y=20

# Swap variables
a, b = b, a

# Variable existence
defined? some_var  # returns nil if not defined

4. Operators

# Arithmetic
+  -  *  /  %  **  # add, subtract, multiply, divide, modulo, exponent

# Comparison
==   !=   >   <   >=   <=   <=>  # <=> returns -1, 0, or 1

# Logical
&&   ||   !    # logical and, or, not
and  or  not   # lower precedence versions

# Assignment shortcuts
x += 5   # x = x + 5
x -= 2
x *= 3
x /= 2
x %= 4
x **= 2

# Special operators
defined?  # check if variable/method defined
..  ...    # range operators
&  |  ^    # bitwise AND, OR, XOR
<<  >>     # bitwise shift

# Safe navigation (Ruby 2.3+)
user&.address&.city  # returns nil if any part missing

5. Conditionals

# If/else
if condition
  # code
elsif other_condition
  # code
else
  # code
end

# Inline if
puts "Yes" if x > 10

# Unless (opposite of if)
unless condition
  # runs if condition is false
end

# Inline unless
puts "No" unless x > 10

# Ternary
result = age >= 18 ? "Adult" : "Minor"

# Case statement
case grade
when "A"
  puts "Excellent"
when "B", "C"
  puts "Good"
else
  puts "Try harder"
end

# Case with expressions
case
when score > 90
  puts "A"
when score > 80
  puts "B"
end

6. Loops & Iterators

# While loop
while condition
  # code
end

# Until loop
until condition
  # code
end

# For loop (rarely used, prefer iterators)
for i in 1..5
  puts i
end

# Times iterator
5.times { |i| puts i }       # 0,1,2,3,4

# Each iterator (most common)
[1,2,3].each { |num| puts num }
(1..5).each do |num|
  puts num
end

# Other iterators
array.each_with_index { |val, idx| puts "#{idx}: #{val}" }
array.map { |x| x * 2 }       # transform
array.select { |x| x.even? }  # filter
array.reject { |x| x.odd? }   # opposite of select

# Loop control
break   # exit loop
next    # skip to next iteration
redo    # repeat current iteration
retry   # restart loop (begin/rescue)

# Infinite loop
loop do
  puts "forever"
  break if condition
end

7. Arrays

# Creation
arr = Array.new(3)        # [nil, nil, nil]
arr = Array.new(3, "x")   # ["x", "x", "x"]
arr = %w[apple banana]    # ["apple", "banana"]
arr = %i[name age]        # [:name, :age]

# Access
arr[0]        # first element
arr[-1]       # last element
arr[1..3]     # range
arr[1, 3]     # start, length

# Modification
arr << 4                 # push
arr.push(5)
arr.unshift(0)           # prepend
arr.pop                  # remove last
arr.shift                # remove first
arr.delete(3)            # delete all 3's
arr.delete_at(2)         # delete index 2
arr.insert(2, 'new')     # insert at index

# Combination
arr1 + arr2              # concatenation
arr1 - arr2              # difference
arr1 & arr2              # intersection
arr1 | arr2              # union

# Useful methods
arr.length               # size
arr.empty?
arr.include?(5)
arr.flatten              # flatten nested arrays
arr.uniq                 # remove duplicates
arr.sort
arr.reverse
arr.sample               # random element
arr.shuffle
arr.join(", ")           # to string

# Array destructuring
first, *rest = [1, 2, 3, 4]  # first=1, rest=[2,3,4]

8. Hashes

# Creation
hash = { name: "Alice", age: 30 }
hash = Hash.new(default_value)
hash = Hash[:a, 1, :b, 2]

# Access
hash[:name]        # "Alice"
hash.fetch(:name)  # with error handling
hash[:missing]     # nil
hash.dig(:a, :b)   # nested hash access

# Modification
hash[:city] = "NYC"
hash.delete(:age)
hash.merge!(other_hash)  # destructive merge

# Iteration
hash.each { |key, value| puts "#{key}: #{value}" }
hash.each_key { |key| puts key }
hash.each_value { |value| puts value }

# Transform
hash.map { |k, v| [k.to_s, v] }.to_h
hash.select { |k, v| v > 18 }
hash.transform_values { |v| v * 2 }

# Default values
hash = Hash.new(0)           # default 0 for missing keys
hash = Hash.new { |h, k| h[k] = [] }  # auto-vivification

# Useful methods
hash.key?(:name)   # check key
hash.value?("Alice")
hash.invert        # swap keys and values
hash.keys
hash.values
hash.length

9. Strings

# Interpolation
name = "Ruby"
puts "Hello #{name}"          # Hello Ruby
puts %Q(Hello #{name})        # alternative
puts %(Hello #{name})         # same

# Special syntax
%q(no interpolation)          # like single quotes
%w(word1 word2)               # array of words
%W(word1 #{name})             # interpolated words

# Manipulation
str = "Hello World"
str.length            # 11
str.upcase            # "HELLO WORLD"
str.downcase
str.capitalize
str.reverse
str.strip             # remove whitespace
str.chomp             # remove newline
str.gsub("World", "Ruby")
str.sub("World", "Ruby")      # first occurrence

# Slicing
str[0]                # "H"
str[0,5]              # "Hello"
str[6..10]            # "World"
str["World"]          # "World"
str[/[A-Z]/]          # "H"

# Splitting/Joining
str.split(" ")        # ["Hello", "World"]
words.join("-")       # "Hello-World"

# Predicates
str.empty?
str.include?("World")
str.start_with?("Hello")
str.end_with?("World")

# Formatting
"%.2f" % 3.1415       # "3.14"
"%03d" % 5            # "005"
"Name: %s, Age: %d" % ["Bob", 25]

# Escaping
puts "Quote: \"Hello\""
puts 'It\'s Ruby'

10. Symbols

# Creation
sym = :name
sym = :"name with spaces"

# Properties
:name.object_id == :name.object_id  # true (immutable)
"name".object_id == "name".object_id  # false

# Conversion
:name.to_s           # "name"
"name".to_sym        # :name

# Use cases
# As hash keys (more efficient than strings)
hash = { :name => "Alice" }  # old syntax
hash = { name: "Alice" }     # new syntax

hash = Hash[[:a, 1], [:b, 2]]

# As method arguments
attr_reader :name, :age

# Checking existence
Symbol.all_symbols.include?(:name)

11. Methods (Functions)

# Basic method
def greet(name)
  return "Hello, #{name}"
end

# Implicit return (last expression)
def add(a, b)
  a + b
end

# Default arguments
def greet(name = "World")
  puts "Hello, #{name}"
end

# Keyword arguments
def create_user(name:, age: 18, city: "NYC")
  { name: name, age: age, city: city }
end
create_user(name: "Alice", city: "LA")

# Variable arguments (*splat)
def sum(*numbers)
  numbers.sum
end
sum(1,2,3,4)  # 10

# Double splat for keyword args
def process(**options)
  options.each { |k,v| puts "#{k}: #{v}" }
end

# Method with block
def repeat(n)
  n.times { yield if block_given? }
end
repeat(3) { puts "Hi" }

# Predicate method (returns boolean)
def even?(num)
  num % 2 == 0
end

# Dangerous/Bang method (modifies object)
def upcase!
  @name = @name.upcase
end

# Method alias
alias new_name old_name

# Method visibility (in classes)
private
protected
public

12. Classes & Objects (OOP)

# Basic class
class Person
  # Attributes
  attr_accessor :name, :age    # getter + setter
  attr_reader :id              # getter only
  attr_writer :secret          # setter only

  # Class variable
  @@population = 0

  # Constant
  SPECIES = "Homo sapiens"

  # Constructor
  def initialize(name, age)
    @name = name
    @age = age
    @@population += 1
  end

  # Instance method
  def introduce
    "I'm #{@name}, #{@age} years old"
  end

  # Class method
  def self.population
    @@population
  end

  # To string
  def to_s
    "Person: #{@name}"
  end

  # Equality
  def ==(other)
    @name == other.name && @age == other.age
  end
end

# Inheritance
class Student < Person
  def initialize(name, age, grade)
    super(name, age)   # call parent constructor
    @grade = grade
  end

  # Override
  def introduce
    super + " and I'm a student"
  end
end

# Usage
alice = Person.new("Alice", 30)
puts alice.name          # getter
alice.age = 31           # setter
puts alice.introduce
puts Person.population

# Class with metaclass methods
class MathUtils
  class << self
    def square(x)
      x * x
    end
  end
end

# Singleton methods (on a single object)
str = "Hello"
def str.shout
  upcase + "!!!"
end

13. Modules & Mixins

# Module as namespace
module MathUtils
  PI = 3.14159
  def self.square(x)
    x * x
  end
end
MathUtils.square(5)   # 25

# Module as mixin (include)
module Swimmable
  def swim
    "Swimming!"
  end
end

module Flyable
  def fly
    "Flying!"
  end
end

class Duck
  include Swimmable
  include Flyable
end

duck = Duck.new
duck.swim   # "Swimming!"
duck.fly    # "Flying!"

# Module with instance methods
module Serializable
  def to_json
    instance_variables.each_with_object({}) do |var, hash|
      hash[var.to_s.delete('@')] = instance_variable_get(var)
    end.to_json
  end
end

# Extend (adds class methods)
module ClassMethods
  def create
    new
  end
end

class Product
  extend ClassMethods
end

Product.create   # class method

# Prepend (overrides methods)
module Logger
  def save
    puts "Logging before save"
    super
    puts "Logging after save"
  end
end

class Document
  prepend Logger
  def save
    puts "Saving document"
  end
end
# Outputs: Logging before save, Saving document, Logging after save

# Include vs Prepend vs Extend
# include: adds as superclass (instance methods)
# prepend: adds before class (overrides)
# extend: adds class methods

14. Blocks, Procs & Lambdas

# Blocks (anonymous, not objects)
def perform
  puts "Before"
  yield if block_given?
  puts "After"
end

perform { puts "Inside block" }

# Block with parameters
def with_params
  yield(5, 10)
end

with_params { |a, b| puts a + b }

# Procs (blocks as objects)
my_proc = Proc.new { |x| puts x * 2 }
my_proc = proc { |x| puts x * 2 }  # shorthand
my_proc.call(5)                     # 10
my_proc[5]                          # 10
my_proc === 5                       # 10 (case w/ procs)

# Lambdas (more strict than procs)
my_lambda = lambda { |x| puts x * 2 }
my_lambda = ->(x) { puts x * 2 }    # stabby lambda
my_lambda.call(5)

# Differences between Proc and Lambda
# 1. Lambda checks argument count, Proc doesn't
lambda { |x| x }.call(1,2)  # Error
proc { |x| x }.call(1,2)    # Ignores extra args

# 2. Return behavior
def test_proc
  proc { return "from proc" }.call
  "after proc"
end

def test_lambda
  lambda { return "from lambda" }.call
  "after lambda"
end

test_proc   # => "from proc" (returns from method)
test_lambda # => "after lambda"

# Passing blocks to methods
def each_with_custom_block(&block)
  [1,2,3].each(&block)
end

# Converting symbol to proc
[1,2,3].map(&:to_s)  # ["1","2","3"]

15. Exception Handling

# Basic rescue
begin
  result = 10 / 0
rescue ZeroDivisionError => e
  puts "Error: #{e.message}"
end

# Multiple rescues
begin
  file = File.open("missing.txt")
rescue Errno::ENOENT => e
  puts "File not found"
rescue StandardError => e
  puts "Other error: #{e.class}"
end

# Ensure (always runs)
begin
  file = File.open("data.txt")
  # process file
rescue
  puts "Error occurred"
ensure
  file.close if file
end

# Raising exceptions
def validate_age(age)
  raise ArgumentError, "Age must be positive" if age <= 0
end

# Custom exception
class MyCustomError < StandardError
  attr_reader :code

  def initialize(message, code)
    super(message)
    @code = code
  end
end

# Retry
attempts = 0
begin
  attempts += 1
  risky_operation
rescue NetworkError
  retry if attempts < 3
end

# Else clause
begin
  # code that might raise
rescue
  # error handling
else
  # runs if no exception
end

# Rescue modifier
result = risky_operation rescue "default value"

16. File I/O

# Reading files
File.open("file.txt", "r") do |file|
  content = file.read
  lines = file.readlines
end

# Shortcuts
content = File.read("file.txt")
lines = File.readlines("file.txt")

# Writing files
File.open("output.txt", "w") do |file|
  file.write("Hello World\n")
  file.puts("Another line")
end

# Append mode
File.open("log.txt", "a") do |file|
  file.puts("New log entry")
end

# Shortcut write
File.write("file.txt", "content")

# File operations
File.exist?("file.txt")
File.delete("file.txt")
File.rename("old.txt", "new.txt")
File.size("file.txt")
File.directory?("dir")
File.file?("file.txt")

# Directory operations
Dir.mkdir("new_dir")
Dir.delete("empty_dir")
Dir.entries(".")           # list contents
Dir.glob("*.rb")           # match files
Dir["*.{rb,erb}"]          # multiple patterns

# Working with paths
require 'pathname'
path = Pathname.new("folder/file.txt")
path.basename      # "file.txt"
path.dirname       # "folder"
path.extname       # ".txt"
path.absolute?
path = Pathname.pwd + "new_file.txt"

# IO streams
$stdin      # standard input
$stdout     # standard output
$stderr     # standard error
STDIN.gets
STDOUT.puts "message"

17. Regular Expressions

# Creation
regex = /pattern/
regex = %r{pattern}
regex = Regexp.new("pattern")

# Matching
if "Hello World" =~ /World/
  puts "Match found"
end

# Match operator
match = "Ruby 2.7" =~ /\d+\.\d+/  # returns index
match = /World/.match("Hello World")

# MatchData object
match = /(\w+):\s(\d+)/.match("Age: 25")
match[0]      # "Age: 25" (full match)
match[1]      # "Age"
match[2]      # "25"
match[:key]   # named capture

# Named captures
pattern = /(?<name>\w+):\s(?<age>\d+)/
match = pattern.match("Bob: 30")
match[:name]  # "Bob"
match[:age]   # "30"

# Scan (find all matches)
"one two three".scan(/\w+/)  # ["one","two","three"]
"a1 b2 c3".scan(/(\w)(\d)/)  # [["a","1"],["b","2"],["c","3"]]

# Substitution
"Hello World".sub(/World/, "Ruby")    # "Hello Ruby"
"Hello World".gsub(/[aeiou]/, '*')    # "H*ll* W*rld"

# Split
"one,two,three".split(/,/)  # ["one","two","three"]

# Common patterns
/\d+/       # digits
/\w+/       # word characters
/\s+/       # whitespace
/./         # any character
/^/         # start of line
/$/         # end of line
/[aeiou]/   # character class
/[^aeiou]/  # negated class
/\A/        # start of string
/\z/        # end of string

# Options
/pattern/i   # case insensitive
/pattern/m   # multiline
/pattern/x   # extended (ignore whitespace)

18. Enumerable Module

# Each (base iterator)
[1,2,3].each { |x| puts x }

# Map / Collect (transform)
[1,2,3].map { |x| x * 2 }        # [2,4,6]
(1..5).collect { |x| x ** 2 }    # [1,4,9,16,25]

# Select / Find All (filter)
[1,2,3,4,5].select { |x| x.even? }  # [2,4]

# Reject (opposite of select)
[1,2,3].reject { |x| x > 2 }        # [1,2]

# Detect / Find (first match)
[1,2,3].find { |x| x > 1 }          # 2

# Reduce / Inject (accumulate)
[1,2,3,4].reduce(0) { |sum, n| sum + n }  # 10
[1,2,3].inject(:*)                           # 6

# Any? / All? / None? / One? (predicates)
[1,2,3].any?(&:even?)      # true (at least one even)
[1,2,3].all?(&:even?)      # false
[1,2,3].none?(&:even?)     # false
[1,2,3].one?(&:even?)      # false (two evens)

# Group By
[1,2,3,4].group_by(&:even?)  # {true=>[2,4], false=>[1,3]}

# Partition
[1,2,3,4].partition(&:even?)  # [[2,4], [1,3]]

# Sort
[3,1,4,2].sort                              # [1,2,3,4]
[3,1,4,2].sort_by { |x| -x }               # [4,3,2,1]

# Chaining
[1,2,3,4,5]
  .select(&:even?)
  .map { |x| x * 10 }
  .reduce(:+)  # 2*10 + 4*10 = 60

# Lazy evaluation (for large collections)
(1..Float::INFINITY)
  .lazy
  .select(&:even?)
  .first(5)  # [2,4,6,8,10]

19. Common Gems

# In Gemfile or install: gem install <name>

# Pry (better IRB)
require 'pry'
binding.pry  # start debugger

# HTTP requests
require 'httparty'
response = HTTParty.get('https://api.example.com')
response.body
response.code

# JSON parsing
require 'json'
hash = JSON.parse('{"name":"Alice"}')
json = {name: "Bob"}.to_json

# Database (ActiveRecord)
require 'active_record'
ActiveRecord::Base.establish_connection(
  adapter: 'sqlite3',
  database: 'db.sqlite3'
)

class User < ActiveRecord::Base
end

# Testing (RSpec)
# spec/my_spec.rb
RSpec.describe "Example" do
  it "works" do
    expect(2 + 2).to eq(4)
  end
end

# Web framework (Sinatra)
require 'sinatra'
get '/' do
  "Hello World"
end

# Web scraping (Nokogiri)
require 'nokogiri'
doc = Nokogiri::HTML(html_string)
doc.css('a').each { |link| puts link['href'] }

# File utilities (FileUtils)
require 'fileutils'
FileUtils.cp('src.txt', 'dest.txt')
FileUtils.mkdir_p('path/to/dir')
FileUtils.rm_rf('directory')

# Date/Time (ActiveSupport)
require 'active_support/all'
1.day.ago
3.hours.from_now
Date.today.beginning_of_week

20. Ruby Style Guide

# Naming
# snake_case for methods and variables
# CamelCase for classes and modules
# SCREAMING_SNAKE_CASE for constants

# Spacing
# 2 spaces indentation (no tabs)
def method
  if condition
    do_something
  end
end

# Method parentheses
# Omit when no arguments
def greet
  puts "hello"
end

# Use when arguments
def add(a, b)
  a + b
end

# Hash syntax
# Use 1.9+ style when keys are symbols
{ name: "Alice", age: 30 }

# String literals
# Prefer single quotes unless interpolation needed
name = 'Ruby'
puts "Hello #{name}"  # interpolation requires double

# Conditional modifiers
puts "Yes" if condition  # inline when short
# Use block form for multi-line

# Avoid `for` loops (use each)
# Avoid `and`/`or` (use `&&`/`||`)

# Use `each` without block returns Enumerator
# Use `map` instead of `collect`
# Use `select` instead of `find_all`

# Freeze mutable constants
ARRAY = [1,2,3].freeze

# Use `%w` for word arrays
%w[apple banana cherry]

# Use `%i` for symbol arrays
%i[name age city]

# Use `_` for unused block parameters
hash.each { |key, _| puts key }

# Comment style
# Single line comment with space after #
# FIXME: fix this later
# TODO: implement this

# Magic comment for UTF-8 encoding
# frozen_string_literal: true
# coding: utf-8

Quick Reference Tips

Task Ruby Code
Run Ruby file ruby filename.rb
Interactive shell irb or pry
Install gem gem install gem_name
Check version ruby -v
Syntax check ruby -c filename.rb
Debug mode ruby -r debug filename.rb
Watch file ruby -w filename.rb

Key Ruby Principles

  1. Everything is an object - even numbers, classes, and nil
  2. Duck typing - "If it walks like a duck..."
  3. Open classes - modify existing classes at runtime
  4. Method missing - powerful metaprogramming
  5. Blocks everywhere - Ruby's secret sauce