Ruby is an object-oriented programming language, which means that it organizes data and behavior into objects. 


        In Ruby, everything is an object, including numbers, strings, and even classes themselves. Object-oriented programming has several key concepts:
  1. Classes: A class is a blueprint for creating objects. It defines the attributes (also called properties or instance variables) and behaviors (also called methods) of objects that belong to it. For example, a Person class might have attributes like name, age, and gender, and behaviors like talk and walk.
  2. Objects: An object is an instance of a class. It has its own values for the attributes defined by the class and can perform the behaviors defined by the class's methods.
  3. Encapsulation: Encapsulation is the practice of keeping data and behavior within a class so that it is not accessible or modifiable from outside the class. This helps to ensure that objects maintain their internal consistency and makes the code easier to maintain.
  4. Inheritance: Inheritance is the ability of a class to inherit attributes and behaviors from another class. This helps to reduce duplication of code and makes the code easier to maintain.
  5. Polymorphism: Polymorphism is the ability of objects of different classes to respond to the same message (method) in different ways. This makes the code more flexible and extensible.

Here is an example of defining a class in Ruby:

    class Person
      attr_accessor :name, :age, :gender

      def initialize(name, age, gender)
        @name = name
        @age = age
        @gender = gender
      end

      def talk
        puts "Hello, my name is #{@name} and I am #{@age} years old."
      end

      def walk
        puts "#{@name} is walking."
      end
    end

        In this example, we define a Person class with three attributes (name, age, and gender) and two methods (talk and walk). The attr_accessor method is used to create getter and setter methods for the attributes so that they can be accessed and modified from outside the class.

We can create an instance of the Person class like this:

    person = Person.new("Alice", 30, "female")
    person.talk # prints "Hello, my name is Alice and I am 30 years old."
    person.walk # prints "Alice is walking."

        This creates a Person object with the name "Alice", age 30, and gender "female". We can then call the talk and walk methods on this object to perform the behaviors defined by the Person class.

Ruby Classes and Objects Examples

        In Ruby, everything is an object and classes are used to create objects. Classes define the properties (also called instance variables) and behaviors (also called methods) of objects that belong to it.

Here is an example of defining a class in Ruby:

    class Person
      attr_accessor :name, :age, :gender

      def initialize(name, age, gender)
        @name = name
        @age = age
        @gender = gender
      end

      def talk
        puts "Hello, my name is #{@name} and I am #{@age} years old."
      end

      def walk
        puts "#{@name} is walking."
      end
    end

        In this example, we define a Person class with three attributes (name, age, and gender) and two methods (talk and walk). The attr_accessor method is used to create getter and setter methods for the attributes so that they can be accessed and modified from outside the class.

We can create an instance of the Person class like this:

    person = Person.new("Alice", 30, "female")
    person.talk # prints "Hello, my name is Alice and I am 30 years old."
    person.walk # prints "Alice is walking."

        This creates a Person object with the name "Alice", age 30, and gender "female". We can then call the talk and walk methods on this object to perform the behaviors defined by the Person class.

Here is another example that demonstrates inheritance, polymorphism, and encapsulation:

    class Animal
      attr_accessor :name, :age

      def initialize(name, age)
        @name = name
        @age = age
      end

      def talk
        raise NotImplementedError, "Subclasses must implement this method"
      end
    end

    class Dog < Animal
      def talk
        puts "Woof!"
      end
    end

    class Cat < Animal
      def talk
        puts "Meow!"
      end
    end

        In this example, we define an Animal class with two attributes (name and age) and a talk method. The talk method is defined with raise NotImplementedError to indicate that it should be implemented in any subclasses of Animal.

        We then define two subclasses of Animal, Dog and Cat, each with their own implementation of the talk method.

We can create instances of these classes and call the talk method on them:

    dog = Dog.new("Fido", 5)
    cat = Cat.new("Fluffy", 3)

    dog.talk # prints "Woof!"
    cat.talk # prints "Meow!"

        This demonstrates how inheritance allows the Dog and Cat classes to inherit the attributes and methods of the Animal class, while polymorphism allows objects of different classes to respond to the same message (talk) in different ways. Encapsulation is also demonstrated by keeping the name and age attributes private and using getter and setter methods (attr_accessor) to access them from outside the class.