Classes
Blueprints for creating objects. They define attributes and methods that objects instantiated from the class will have. Instance variables are prefixed with @. Class variables are prefixed with @@
class Person
@@species = "homo sapien" # class var
attr_accessor :name, :age # provides getter and setter methods
def initialize(name, age)
@name = name # instance var
@age = age
end
class << self
def species # exposes class variable
@@species
end
end
end
person = Person.new("Alice", 25)
puts "I am a #{Person.species}"Methods
Different types of methods include: instance methods, class methods, and module methods.
class Person
attr_accessor :name
def initialize(name)
@name = name
end
# instance methods operate on the instance
def greet
"Hi, I'm #{@name}."
end
class << self # class methods operate on the class itself
def info # self refers to the class
"This is a Person class."
end
end
end
person = Person.new("Alice")
puts person.greet # Hi, I'm Alice.
puts Person.info # This is a Person class.Modules (mixins)
A way to organize and namespace our classes
Modules can be mixed into classes using include (as instance method), extend (as class method), or prepend (as instance methods that override local instance methods)
prepend methods technically don’t override local methods, they just appear earlier in the method lookup
module Flyable
def fly # module method
"I can fly!"
end
end
class Bird
include Flyable # module method will operate on instance
end
class Plane
extend Flyable # module method will operate on class
end
class Kite
prepend Flyable
def fly
"I can fly if there is wind"
end
class << self
def fly
"Kites can fly!"
end
end
end
bird = Bird.new
kite = Kite.new
puts bird.fly # I can fly!
puts Plane.fly # I can fly!
puts kite.fly # I can fly!
puts Kite.fly # Kites can fly!Singleton Methods
A method on a specific instance
kite = Kite.new
def kite.crash
"We are stuck in a tree!"
end
puts kite.crash # we are stuck in a tree
puts Kite.crash # NoMethodErrorSelf
class MyClass
puts self # MyClass
def instance_method
puts self # instance of MyClass
end
def self.class_method
puts self # MyClass
end
endInheritance
A class can inherit from another using <. super calls the parent method with the same name
class Animal # parent class
attr_reader :species
def initialize(species)
@species = species
end
end
class Dog < Animal # child class
attr_reader :name
def initialize(name)
super("Dog") # calls Animal's initialize with provided args
# super() calls with no args
# super calls with current args (name)
@name = name
end
end
dog = Dog.new("Buddy")
puts "#{dog.species} #{dog.name}" # Dog BuddyPolymorphism/Overriding
A subclass can redefine methods from its parent class
class Animal
def speak
"Some generic sound"
end
end
class Cat < Animal
def speak
"Meow!"
end
end
animals = [Animal.new, Cat.new]
animals.each do |animal|
puts animal.speak # Some generic sound, Meow!
endEncapsulation
Methods may be:
- Public: default, accessible anywhere
- Private: can only be called within the class
- Protected: can be accessed by instances of the same class or its subclasses
class Person
def initialize(name, secret)
@name = name
@secret = secret
end
def introduce
"Hi, I'm #{@name}."
end
private # methods below this are private
def reveal_secret
@secret
end
end
person = Person.new("Alice", "I love Ruby.")
puts person.introduce # Hi, I'm Alice.
puts person.reveal_secret # errorsReusing Code
Blocks
Chunks of code that can be passed to methods
# implicit block
def greet_with_block(name)
yield(name) if block_given? # yields to the block, if block is given
end
# explicit block
def greet_with_block(name, &block) # &block represents the proc object
block.call(name) if block
end
greet_with_block("Alice") { |name| puts "Hello, #{name}!" } # Hello, Alice!Procs and Lambdas
Procs are objects that hold blocks of code
add = proc { |a, b| a + b }
puts add.call(2, 3) # 5
puts add.call(2) # TypeError 2 + nilProc shorthand (:&)
arr.map(&:to_s) # same as arr.map { |item| item.to_s }Lambdas are Procs with strict argument checking
add = ->(a, b) { a + b }
puts add.call(2, 3) # 5
puts add.call(2) # errors, wrong number of argumentsManipulating Built ins
Built in methods can be overridden to customize behavior
class Person
def initialize(name, age)
@name = name
@age = age
end
def to_s # string representation
"Name: #{@name}, Age: #{@age}"
end
def inspect # used for debugging
"#<Person: #{@name}, #{@age}>"
end
end
person = Person.new("Alice", 30)
puts person.to_s # Name: Alice, Age: 30
puts person.inspect # #<Person: Alice, 30>Metaprogramming
Metaprogramming refers to generating or modifying code at runtime
Runtime: when the program is executing, as opposed to when it is written or statically analyzed
instance_eval
Opens instance context. Then we can access, add, and modify instance variables
class Person
def initialize(name)
@name = name
end
end
person = Person.new("Alice")
person.instance_eval do
puts @name # Alice
@age = 25 # sets instance variable
endclass_eval
Opens class context. Then we can access, add, and modify class variables and methods
Person.class_eval do
def greet
"Hi, I'm #{@name}"
end
end
puts person.greet # Hi, I'm Alicedefine_method
Create dynamic methods
class User
[:name, :email, :age].each do |attr|
define_method(attr) do # create getter method
instance_variable_get("@#{attr}")
end
define_method("#{attr}=") do |value| # create setter
instance_variable_set("@#{attr}", value)
end
end
end
user = User.new
user.name = "Bob"
puts user.name # Bobsend and public_send
Call methods dynamically
class Calculator
def add(a, b)
a + b
end
private
def secret
"secret!"
end
end
calc = Calculator.new
# we can dynamically
# decide which method to call
method_name = :add
puts calc.send(method_name, 2, 3) # 5
puts calc.send(:secret) # secret!, ignores private
puts calc.public_send(:secret) # NoMethodError, respects private