Contents

Ruby Methods

Ruby Methods

A method in Ruby is a collection of statements that perform a specific task and return a result. Methods help to avoid repetition, allowing code reuse by defining the logic once and calling it multiple times. Methods in Ruby are defined using the def keyword, and the method definition is ended with the end keyword. Method names in Ruby are typically in lowercase.

Defining and Calling a Method

A method in Ruby must be defined before it is called. It can be called simply by using its name.

Syntax:

				
					def method_name
  # Statement 1
  # Statement 2
end

				
			

Example:

				
					# Ruby program to demonstrate defining and calling a method

# Here 'welcome' is the method name
def welcome
  # Statement to be displayed
  puts "Hello! Welcome to Ruby Programming."
end

# Calling the method
welcome

				
			

Output:

				
					Hello! Welcome to Ruby Programming.

				
			
Passing Parameters to Methods

In Ruby, parameters are passed to methods by simply listing them within parentheses. Default values can also be provided for parameters.

Syntax:

				
					def method_name(param1, param2)
  # Statements
end

				
			

Example:

				
					# Ruby program to demonstrate passing parameters to a method

# Defining the method 'display_info' with two parameters
def display_info(name = "Ruby", level = "Beginner")
  puts "Language: #{name}"
  puts "Level: #{level}"
end

# Calling the method with parameters
display_info "Python", "Intermediate"

puts ""

# Calling the method without passing parameters
display_info

				
			

Output:

				
					Language: Python
Level: Intermediate

Language: Ruby
Level: Beginner

				
			
Variable Number of Parameters

Ruby allows methods to accept a variable number of parameters. This is useful when the exact number of arguments is unknown at the time of defining the method. The * symbol is used to define a variable number of parameters.

Syntax:

				
					def method_name(*args)
  # Statements
end

				
			

Example:

				
					# Ruby program to demonstrate a method that takes a variable number of arguments

# Defining the method 'show_items' that can take any number of arguments
def show_items(*items)
  puts "Number of items: #{items.length}"
  
  # Display each item using a loop
  items.each_with_index do |item, index|
    puts "Item #{index + 1}: #{item}"
  end
end

# Calling the method with multiple arguments
show_items "Apple", "Banana", "Cherry"

# Calling the method with a single argument
show_items "Orange"

				
			

Output:

				
					Number of items: 3
Item 1: Apple
Item 2: Banana
Item 3: Cherry
Number of items: 1
Item 1: Orange

				
			

Return Statement in Methods

The return statement is used to return one or more values from a method. By default, a method returns the value of the last executed statement. The return keyword is optional and can be omitted unless you want to explicitly specify the return values.

Example:

				
					# Ruby program to demonstrate the return statement in a method

# Defining the method 'calculate_sum'
def calculate_sum
  # Variables within the method
  num1 = 25
  num2 = 75

  # Calculating sum
  sum = num1 + num2

  # Returning the sum
  return sum
end

# Calling the method and printing the result
puts "The sum is: #{calculate_sum}"

				
			

Output:

				
					The sum is: 100

				
			

Method Visibility in Ruby

In Ruby, method visibility determines how methods in a class can be accessed. Methods can be declared as public, private, or protected. By default, methods are public unless explicitly specified otherwise. Method visibility is used to achieve data abstraction, meaning it shows only the necessary details while hiding the internal implementation.

Access Modifiers in Ruby

Ruby provides three types of access modifiers to control method visibility:

  • Public Access Modifier
  • Protected Access Modifier
  • Private Access Modifier

1. Public Access Modifier: Methods defined with public are accessible from any part of the program. By default, all methods in a class are public unless specified otherwise.

Example 1:

				
					# Program to demonstrate public access modifier

class Example
  # Methods are public by default
  def public_method1
    puts "public_method1 called!"
  end

  # Using the public keyword explicitly
  public

  def public_method2
    puts "public_method2 called!"
  end
end

# Creating an object
obj = Example.new

# Calling methods
obj.public_method1
obj.public_method2

				
			

Output:

				
					public_method1 called!
public_method2 called!

				
			

In this example, two public methods (public_method1 and public_method2) are defined in the Example class and can be called from outside the class.

2. Protected Access Modifier: Methods defined as protected can only be called within the class they are defined in and by instances of that class or its subclasses.

Example 2:

				
					# Program to demonstrate protected access modifier

# Superclass
class Parent
  # Using the protected keyword
  protected

  # Protected method
  def protected_method
    puts "protected_method called!"
  end
end

# Subclass
class Child < Parent
  def public_method
    # Calling the protected method from within the subclass
    self.protected_method
  end
end

# Creating an object of the subclass
obj = Child.new

# Calling the public method which in turn calls the protected method
obj.public_method

				
			

Output:

				
					protected_method called!

				
			

Here, the protected_method in the Parent class is called within the public_method of the Child subclass.

3. Private Access Modifier: Private methods can only be called within the class they are defined in. They cannot be called with an explicit receiver, even if that receiver is self.

Example 3:

				
					# Program to demonstrate private access modifier

class Example
  # Using the private keyword
  private

  # Private method
  def private_method
    puts "private_method called!"
  end

  # Public method
  public

  def public_method
    # Calling the private method within the public method
    private_method
  end
end

# Creating an object
obj = Example.new

# Calling the public method which in turn calls the private method
obj.public_method

				
			

Output:

				
					private_method called!

				
			

In this example, private_method is a private method and can only be called within the class. It is invoked from the public_method of the same class.

Method Visibility in Action

Here is an example demonstrating method visibility by defining multiple public, protected, and private methods in a superclass and a subclass.

Example 4:

				
					# Program to illustrate method visibility

# Superclass
class Parent
  private

  # Private method
  def private_method
    puts "private_method called!"
  end

  protected

  # Protected method
  def protected_method
    puts "protected_method called!"
  end

  public

  # Public methods
  def public_method1
    puts "public_method1 called!"
  end

  def public_method2
    # Calling protected and private methods within a public method
    protected_method
    private_method
  end
end

# Subclass
class Child < Parent
  # Public method
  def public_method3
    # Calling protected method from within the subclass
    protected_method
  end
end

# Creating objects
obj1 = Parent.new
obj2 = Child.new

# Calling methods
puts "\nParent methods: \n"
obj1.public_method1
obj1.public_method2

puts "\nChild methods: \n"
obj2.public_method1
obj2.public_method3

				
			

Output:

				
					Parent methods: 
public_method1 called!
protected_method called!
private_method called!

Child methods: 
public_method1 called!
protected_method called!

				
			

Recursion in Ruby

Recursion is a process where a function calls itself directly or indirectly. The function that performs this operation is called a recursive function. Recursion is beneficial because it breaks down problems into smaller, more manageable sub-problems, resulting in cleaner and more efficient code. In Ruby, we can use loops to repeat actions, but recursion offers an elegant alternative in many situations.

Why Use Recursion?

In Ruby, recursion plays a significant role in solving real-world problems by introducing a way to handle complex data through recursive functions. Although loops can achieve similar results, recursion often provides a more intuitive and elegant solution.

Table of Contents

1. Iterative Code
2. Recursive Code
3. Benefits and Limitations of Recursion

1. Iterative Code: One of the simplest problems we can solve iteratively is summing up the elements in an array. In an iterative approach, a loop is used to add each element to a cumulative total.

Example:

				
					# Iterative method to sum an array of numbers
def iterative_sum(numbers)
  sum = 0
  numbers.each do |number|
    sum += number
  end
  sum
end

# Calling the method and printing the result
puts iterative_sum([1, 2, 3, 4, 5])

				
			

Output:

				
					15

				
			

2. Recursive Code: In a recursive solution, the function calls itself with a subset of the problem until a base case is met. For summing an array, the base case occurs when the array is empty.

Example:

				
					# Recursive method to calculate the sum of all numbers in an array
def recursive_sum(numbers)
  # Base Case: If the array is empty, return 0
  return 0 if numbers.empty?

  # Recursive call: Adding each element to the total by calling the method again
  sum = numbers.pop
  return sum + recursive_sum(numbers)
end

puts recursive_sum([1, 2, 3, 4, 5])

				
			

Output:

				
					15

				
			

Understanding Recursive Steps: Factorial Calculation

To understand recursion more deeply, let’s calculate the factorial of a number using a recursive method. The factorial of a number is the product of all positive integers less than or equal to that number.

Example:

				
					# Ruby code for calculating the factorial of a number recursively
def recursive_factorial(n)
  # Base Case: Return 1 if n is 0 or 1
  return 1 if n <= 1

  # Recursive call: Multiply current number with the factorial of (n-1)
  return n * recursive_factorial(n - 1)
end

# Calling the method
puts recursive_factorial(5)

				
			

Example:

				
					120

				
			

Explanation:
The method first calls itself with the number 5, which in turn calls itself with 4, then 3, and so on until it reaches 1. The calculations proceed as follows:

				
					recursive_factorial(5) = 5 * recursive_factorial(4)
                       = 5 * 4 * recursive_factorial(3)
                       = 5 * 4 * 3 * recursive_factorial(2)
                       = 5 * 4 * 3 * 2 * recursive_factorial(1)
                       = 5 * 4 * 3 * 2 * 1
                       = 120

				
			

Recursive Code Example: Fibonacci Number Calculation

Another classic example of recursion is the calculation of the nth Fibonacci number. The base cases occur when n is less than 2, where the Fibonacci number is simply n. Otherwise, the Fibonacci number is the sum of the two preceding ones.

Example:

				
					# Recursive method to calculate the nth Fibonacci number
def recursive_fibonacci(n)
  return n if n < 2

  # Recursive call: Sum of (n-1)th and (n-2)th Fibonacci numbers
  recursive_fibonacci(n - 1) + recursive_fibonacci(n - 2)
end

puts recursive_fibonacci(5)

				
			

Output:

				
					5

				
			

3. Benefits and Limitations of Recursion

Benefits:

  • Recursion can simplify the solution of problems by breaking them into smaller sub-problems.
  • It often leads to cleaner and more readable code for complex tasks like tree traversal, factorials, Fibonacci sequences, and more.

Limitations:

  • Recursion in Ruby can lead to a SystemStackError: stack level too deep when dealing with large inputs.
  • For problems with large datasets, recursion may be less efficient than iterative solutions due to the risk of stack overflow.

Ruby Hook Methods

Ruby Hook Methods are special methods that are triggered in response to certain actions, such as adding a method, including a module, or subclassing a class. They allow the extension of the behavior of objects and classes at runtime. These methods are not predefined, but programmers can define and use them to customize class or module behavior dynamically.

Hook methods enable a more flexible approach to modify and extend the fundamental workings of methods, modules, and classes in Ruby.

Common Ruby Hook Methods

1. Included
2. Prepended
3. Extended
4. Inherited
5. method_missing

Modules in Ruby

Before delving into the hook methods, it’s essential to understand modules in Ruby. Modules are containers for methods and constants that can be mixed into classes. This allows for code reuse and modularity. Hook methods often interact with modules to alter or extend class behavior.

1. Included: The included method is called when a module is included in another class or module. It is used to apply the contents of a module to instances of a class. When the module is included, the included method executes automatically.

Example:

				
					# Declaring a module
module WelcomeMessage
  def self.included(target)
    puts "The #{target} has been greeted with a warm welcome!"
  end
end

# Class where the module is included
class User
  include WelcomeMessage # Using the include statement
end

				
			

Output:

				
					The User has been greeted with a warm welcome!

				
			

2. Prepended: Introduced in Ruby 2.0, prepend works similarly to include, but with a slight difference. It allows you to override methods defined in a class with those from the prepended module. This concept involves altering the target class’s behavior by using methods from the module.

Example:

				
					# Module to demonstrate prepend method
module Language
  def self.prepended(target)
    puts "#{self} has been prepended to #{target}"
  end

  def description
    "The language used is Ruby."
  end
end

# Class where the module is prepended
class Programming
  prepend Language # The module Language is prepended
end

# Calling the method
puts Programming.new.description

				
			

Output:

				
					Language has been prepended to Programming
The language used is Ruby.

				
			

3. Extended: The extended method is used when you want to mix a module’s methods into a class itself, not just into its instances. Unlike include, which mixes methods into an instance, extend applies methods directly to the class.

Example:

				
					# Module to demonstrate the extend method
module Framework
  def self.extended(target)
    puts "#{self} was extended by #{target}"
  end

  def description
    "This framework is based on Ruby."
  end
end

# Class where the module is extended
class Software
  extend Framework # Extending the module Framework
end

# Calling the method
puts Software.description

				
			

Output:

				
					Framework was extended by Software
This framework is based on Ruby.

				
			

4. Inherited: The inherited hook method is called when a class is subclassed. This is particularly useful in object-oriented programming when a subclass inherits attributes and methods from its parent class.

Example:

				
					# Parent class
class Vehicle
  def self.inherited(subclass)
    puts "#{subclass} is a subclass of Vehicle"
  end
end

# Subclass
class Car < Vehicle
end

				
			

Output:

				
					Car is a subclass of Vehicle

				
			

5. method_missing:The method_missing hook method is called when an object is sent a message (method call) that it does not understand (i.e., when a method that doesn’t exist is called on an object).

Example:

				
					# Main class
class Language
  def method_missing(method_name, *args)
    "#{method_name} is not defined for #{self}"
  end

  def known_method
    "This method is defined."
  end
end

instance = Language.new

# Calling a method that exists
puts instance.known_method

# Calling a method that does not exist
puts instance.unknown_method

				
			

Output:

				
					This method is defined.
unknown_method is not defined for #<Language:0x0000000001234567>

				
			

Ruby Range Class Methods

In Ruby, the Range class allows you to represent a set of values, defined by a starting and an ending point. Ranges can be created using a literal (start..end for inclusive ranges or start...end for exclusive ranges) or by using Range.new.

Here are the key concepts and examples for Ruby ranges:

Creating Ranges

You can create a range with the following syntaxes:

  • Inclusive Range (..): Includes the end value.
  • Exclusive Range (...): Excludes the end value.
				
					(1..6).to_a    # => [1, 2, 3, 4, 5, 6]
(1...6).to_a   # => [1, 2, 3, 4, 5]

				
			
Class Method

Range.new(start, end, exclusive = false): This class method creates a new range. If the third parameter is true, the range excludes the end value; otherwise, it includes the end.

				
					a = Range.new(1, 5)
b = Range.new(1, 5)
puts a == b   # => true

				
			
Instance Methods
  • ==: Checks if two ranges are equal by comparing their start, end, and exclusion flag.
				
					case 30
when 1...50
  puts "In range"
else
  puts "Out of range"
end
# Output: In range

				
			
  • begin: Returns the first object in the range.
				
					range = (3..10)
puts range.begin   # => 3

				
			
  • end: Returns the last object in the range.
				
					range = (3..10)
puts range.end   # => 10

				
			
  • each: Iterates over each element of the range.
				
					(1..5).each { |i| print i, " " }
# Output: 1 2 3 4 5

				
			
  • first: Returns the first object in the range (same as begin).
				
					range = (5..10)
puts range.first   # => 5

				
			
  • last: Returns the last object in the range (same as end).
				
					range = (5..10)
puts range.last    # => 10

				
			
  • member? or include?: Checks if a value is a member of the range.
				
					range = (1..10)
puts range.member?(5)    # => true
puts range.include?(15)  # => false

				
			
  • exclude_end?: Returns true if the range excludes the end value, false otherwise.
				
					range = Range.new(1, 5, true)
puts range.exclude_end?   # => true

				
			
  • step: Iterates over the range, stepping by a given value.
				
					(1..10).step(2) { |i| print i, " " }
# Output: 1 3 5 7 9

				
			
				
					true
false