๐งฑ OOP in Ruby for Rails Developers
๐ Detailed Explanation: OOP in Ruby Made Simple
Object-Oriented Programming (OOP) is a way to structure your code by organizing it into โobjectsโ. In Ruby, **everything is an object** โ numbers, strings, even classes themselves.
๐ง What is an Object?
An object is like a real-world thing. It has:
- Data (Attributes): Like a personโs name or age
- Behavior (Methods): Like walking or talking
๐๏ธ What is a Class?
A class is like a blueprint. For example:
class Dog
def bark
"Woof!"
end
end
This doesnโt create a dog yet. It’s just the idea of a dog.
๐งช Creating an Object
Now let’s create a real dog using the class:
dog = Dog.new
puts dog.bark # => \"Woof!\"
๐งฑ Why Use OOP?
- ๐จโ๐ง Organize your code better
- โป๏ธ Reuse code across your app
- ๐งช Make testing and debugging easier
- ๐ Protect your data using private methods
- ๐ Scale your app easily as it grows
๐ก Example in a Rails App
In Rails, everything you use is object-oriented:
User.find(1)
is an object calling a method@user.name
is calling thename
method on an object- Each model, controller, and helper is a class
โ Summary
OOP helps you write clean, organized, and powerful Ruby code. It’s not just a concept โ it’s how Ruby and Rails are built from the ground up.
๐ Key Terms and Best Concepts in OOP (Ruby)
๐งฉ Term | ๐ Description |
---|---|
Class | A blueprint for creating objects. Example: class User |
Object | An instance of a class. It holds data and methods. |
Method | A function defined inside a class that performs actions. Example: def greet |
Instance Variable | A variable that holds data inside an object, prefixed with @ . Example: @name |
Initialize | Special method called when an object is created. Acts like a constructor. Example: def initialize(name) |
Encapsulation | Hides internal details and keeps data safe. Achieved using private/protected methods. |
Inheritance | One class can inherit behavior from another. Use < symbol. Example: class Admin < User |
Polymorphism | Different classes can define the same method in their own way. |
Module | A collection of methods that can be included in classes using include . |
self | Refers to the current object or class context. Often used in class methods. |
super | Calls the same method from the parent class. Used in overridden methods. |
attr_accessor | Creates getter and setter methods for instance variables. |
Private | Methods not accessible outside the class or object context. |
Duck Typing | โIf it walks like a duck and quacks like a duck, it’s a duck.โ Focus on behavior, not class. |
Dynamic Dispatch | Ruby determines method calls at runtime, allowing flexible code behavior. |
๐ Flow of OOP in Ruby & Where Itโs Used
๐งญ Step-by-Step Flow โ How OOP Works in Ruby
- Define a Class:
Create a class to describe the structure and behavior of the object. - Add Attributes & Methods:
Use instance variables (e.g.,@name
) and methods (e.g.,def speak
). - Initialize with Data:
Useinitialize
to pass data when creating the object. - Create Object (Instance):
UseClassName.new
to create an object. - Call Methods:
Use the object to access methods or data (e.g.,user.greet
). - Extend with Inheritance or Modules:
Use inheritance (<
) or modules (include
) to reuse logic. - Use in Application Logic:
Use objects in controllers, services, and models in Rails.
๐ Real-World Use Areas in Ruby & Rails
- ๐ฅ Models in Rails: Each model (e.g.,
User
) is a class that behaves like an object - ๐ Service Objects: Business logic wrapped inside reusable classes
- ๐ฆ Background Jobs: Sidekiq/ActiveJob uses job classes and workers
- ๐ฎ Mailers: Email logic wrapped in object-oriented classes
- ๐ Controllers: Each controller is a class handling request-response logic
- ๐ Form Objects: Classes used to combine multiple models or validations
- ๐ Policy Objects: Used in authorization frameworks like Pundit
- ๐งช Testing: Objects are easier to mock and test individually
- ๐ Serializers: Object representations of your data for APIs (e.g., ActiveModel::Serializer)
- ๐ง Custom Validators: Use classes to define custom validation logic
โ Summary
OOP isnโt just theory in Ruby โ itโs the core of how your Rails app is structured. From models to services, everything is built as objects to make the app reusable, clean, and powerful.
๐ Gems and Libraries That Support OOP in Ruby
Ruby and Rails provide built-in support for Object-Oriented Programming, but the following gems and libraries can help you write cleaner, more modular, and testable OOP code:
๐งฉ Gem / Library | ๐ Purpose / Description |
---|---|
dry-rb | A collection of gems (like dry-struct , dry-types , dry-validation ) that help you build modular, immutable, and typed objects. |
trailblazer | Helps you organize business logic into objects like operations, cells, and contracts (ideal for large OOP Rails apps). |
reform | Form objects gem that keeps validations out of models. Promotes separation of concerns via OOP principles. |
interactor | Encapsulates a single unit of business logic in a class called an interactor. Encourages clean OOP service objects. |
virtus (deprecated) | Used to build plain old Ruby objects (POROs) with attributes โ replaced largely by dry-struct . |
active_model_serializers | Turns your models into structured JSON objects โ helpful for API object representation. |
pundit | Provides object-oriented policies for authorization logic. Follows OOP patterns with Policy classes. |
simple_command | Service object helper gem to encapsulate logic in clean command-style objects. |
dry-monads | Encourages object-oriented flow control (success/failure) in functional or OOP style. |
rspec-rails | Encourages OOP-style specs and mocking by testing behavior of objects, not just output. |
๐ฆ Example Gemfile Snippet
# Gemfile
gem 'dry-struct'
gem 'reform'
gem 'interactor'
gem 'pundit'
gem 'simple_command'
These libraries are not required to use OOP in Ruby, but they promote better separation of concerns, testing, and modular design โ all essential aspects of object-oriented programming.
๐งฑ Best Implementation of a Class in Ruby (With Real-World Use)
๐งญ Goal
Build a User class that stores name and email, validates the data, and can greet the user or check if the email is valid.
1๏ธโฃ Define the Class and Attributes
class User
attr_accessor :name, :email
def initialize(name, email)
@name = name
@email = email
end
attr_accessor
automatically creates getter/setter methods.initialize
is the constructor, called when we create a new object.@name
and@email
are instance variables.
2๏ธโฃ Add Instance Methods (Behavior)
def greet
\"Hello, #{@name}!\"
end
def valid_email?
email.include?('@') && email.end_with?('.com')
end
end
greet
uses data inside the object.valid_email?
checks email formatting.
3๏ธโฃ Create and Use the Object
# Creating object
user = User.new(\"Ali\", \"ali@example.com\")
# Calling methods
puts user.greet # => \"Hello, Ali!\"
puts user.valid_email? # => true
4๏ธโฃ Add Input Validation (Optional)
class User
attr_accessor :name, :email
def initialize(name, email)
raise ArgumentError, \"Name can't be blank\" if name.strip.empty?
raise ArgumentError, \"Invalid email\" unless email.include?('@')
@name = name
@email = email
end
end
This ensures bad data is caught early.
โ Best Practices
- Use
attr_reader
/attr_writer
for read-only/write-only access when needed. - Encapsulate logic inside methods, not in scripts or controllers directly.
- Keep classes focused on one responsibility (Single Responsibility Principle).
- Validate input early and raise clear exceptions.
- Keep instance variables private unless there’s a reason to expose them.
๐ Real-World Use Case (Rails)
In Rails, models are classes. Example: User < ApplicationRecord
class User < ApplicationRecord
validates :name, presence: true
validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
def greet
\"Welcome, \#{name}\"
end
end
This keeps your logic organized and your Rails app clean and testable.
๐ฆ Best Implementation of a Module in Ruby (With Real-World Use)
๐งญ Goal
Create a Sharable module that can be included in any class to provide social media sharing functionality. This promotes code reuse without inheritance.
๐ What is a Module?
- A module is a container for methods and constants.
- It cannot be instantiated like a class.
- Modules are used for namespacing or mixing in behavior via
include
orextend
.
1๏ธโฃ Define the Module
module Sharable
def share(platform)
\"Shared on \#{platform.capitalize} by \#{@name}!\"
end
end
This module defines one reusable method called share
.
2๏ธโฃ Include Module in a Class
class User
include Sharable
attr_reader :name
def initialize(name)
@name = name
end
end
include
adds instance methods from the module to the class.User
now has access to theshare
method.
3๏ธโฃ Use the Object with Mixed-In Methods
user = User.new(\"Ayesha\")
puts user.share(\"twitter\") # => \"Shared on Twitter by Ayesha!\"
4๏ธโฃ Extend the Module for Class Methods
module Logger
def log(message)
puts \"[LOG] \#{message}\"
end
end
class App
extend Logger
end
App.log(\"Application started\") # => [LOG] Application started
extend
adds module methods as class methods.
๐ง When to Use a Module
- To share behavior across unrelated classes
- To avoid duplication of utility logic
- To organize related methods together
- As a lightweight alternative to inheritance
โ Best Practices
- Use
include
for instance methods andextend
for class methods. - Group related behavior logically (e.g., Exportable, Sharable, Auditable).
- Donโt put state (like instance variables) in modules unless you control the context.
- Use modules for mixins, not for core business logic.
- Avoid deep module nesting unless you’re building a gem or namespacing APIs.
๐ Real-World Use Case (Rails)
Modules are used extensively in Rails:
ActiveModel::Validations
โ adds validation methods to modelsDevise::Models
โ mixin authentication methodsConcern
โ Rails DSL for modularizing reusable code in models/controllers
๐ฆ Example: Using Concern in Rails
# app/models/concerns/archivable.rb
module Archivable
extend ActiveSupport::Concern
def archive!
update(archived: true)
end
end
# app/models/post.rb
class Post < ApplicationRecord
include Archivable
end
Now every Post
can call post.archive!
.
๐ฏ Summary
Modules in Ruby help make your code **modular**, **DRY**, and **extensible** โ especially when you want to share functionality without inheritance. They’re essential to both Ruby and Rails design patterns.
๐ Best Implementation of Mixins in Ruby (With Practical Use)
๐งญ Goal
Use a **mixin** to add shared functionality (like logging) across multiple classes, without using inheritance.
๐ What is a Mixin?
- A **mixin** in Ruby is when you use a
module
to add reusable methods to one or more classes. - This is done using
include
(for instance methods) orextend
(for class methods). - Unlike classes, you can include multiple modules into one class โ allowing flexible code reuse.
1๏ธโฃ Define a Mixin Module
module LoggerMixin
def log_action(action)
puts \"[LOG] \#{action} at \#{Time.now}\"
end
end
This module defines a reusable method for logging actions.
2๏ธโฃ Include It in a Class
class PaymentProcessor
include LoggerMixin
def process(amount)
log_action(\"Processing payment of $#{amount}\")
# Payment logic...
end
end
3๏ธโฃ Create and Use the Object
processor = PaymentProcessor.new
processor.process(99.99)
# Output:
# [LOG] Processing payment of $99.99 at 2025-05-29 12:00:00
4๏ธโฃ Add Another Class Using the Same Mixin
class UserNotifier
include LoggerMixin
def send_email(user)
log_action(\"Sending email to #{user}\")
# Email logic...
end
end
notifier = UserNotifier.new
notifier.send_email(\"ayesha@example.com\")
Now both classes reuse the same log_action
method!
โ Best Practices for Mixins
- โ Use for shared behavior, not shared data.
- โ Group logically related methods together.
- โ Prefer modules over inheritance for reusable logic.
- โ Avoid using instance variables inside modules unless controlled carefully.
- โ Prefix method names uniquely in large apps to avoid clashes.
๐ Mixin vs Inheritance
Mixins | Inheritance |
---|---|
Include multiple modules | Only one parent class allowed |
Flexible & reusable | Rigid structure |
Good for behavior | Good for hierarchy |
๐ Real-World Rails Example: ActiveSupport::Concern
# app/models/concerns/loggable.rb
module Loggable
extend ActiveSupport::Concern
def log_record(message)
Rails.logger.info \"[LOG] \#{message}\"
end
end
# app/models/order.rb
class Order < ApplicationRecord
include Loggable
after_create { log_record(\"Order ##{id} created\") }
end
Using mixins like this helps separate concerns and keeps models clean.
๐ฏ Summary
Mixins let you share methods across multiple classes using modules. Theyโre a powerful way to organize and reuse code in Ruby and Rails applications.
๐งพ Best Way to Handle Attributes in Ruby Classes (With Explanation)
๐งญ Goal
Define and manage object attributes in a clean, controlled, and Ruby-idiomatic way using attr_reader
, attr_writer
, and attr_accessor
.
1๏ธโฃ Basic Attribute Access with attr_accessor
class Product
attr_accessor :name, :price
def initialize(name, price)
@name = name
@price = price
end
end
product = Product.new(\"Shoes\", 49.99)
puts product.name # => Shoes
product.price = 59.99
puts product.price # => 59.99
attr_accessor
creates both getter and setter methods.- Use it when you want to read and write the value from outside the class.
2๏ธโฃ Use attr_reader for Read-Only Attributes
class User
attr_reader :email
def initialize(email)
@email = email
end
end
user = User.new(\"test@example.com\")
puts user.email # => test@example.com
# user.email = \"new@example.com\" โ Error: undefined method
- Protects the attribute from being changed outside the class.
- Use
attr_reader
when the value should only be set once (e.g., ID, email).
3๏ธโฃ Use attr_writer for Write-Only Attributes (Rare)
class PasswordManager
attr_writer :password
def verify(input)
input == @password
end
end
manager = PasswordManager.new
manager.password = \"secret123\"
- Use when a value should be written but not read publicly (e.g., passwords).
4๏ธโฃ Custom Setters with Validation or Logic
class Invoice
attr_reader :amount
def amount=(value)
raise \"Amount must be positive\" if value <= 0
@amount = value
end
end
invoice = Invoice.new
invoice.amount = 100 # โ
invoice.amount = -5 # โ raises error
- Custom setters allow you to add business rules when setting a value.
- Great for sanitizing, formatting, or validating inputs.
5๏ธโฃ Freeze Immutable Attributes
class Config
attr_reader :mode
def initialize(mode)
@mode = mode.freeze
end
end
Freezing values ensures they can’t be modified after being set.
โ Best Practices
- Use
attr_reader
for constants and immutable attributes. - Use
attr_writer
sparingly โ mostly for sensitive data. - Use
attr_accessor
only when both reading and writing are safe. - Prefer custom setters when validation or side effects are needed.
- Keep instance variables private whenever possible.
- Document attribute behavior clearly if custom logic is added.
๐ Real-World Rails Example
Rails models automatically expose database fields as attributes. But for non-DB attributes:
class Report
attr_accessor :title
def initialize(title)
@title = title
end
def printable_title
title.strip.titleize
end
end
๐ฏ Summary
Ruby provides elegant, flexible ways to manage attributes through attr_reader
, attr_writer
, and attr_accessor
. Choose the right tool based on the level of control you need. For public interfaces, always prefer clarity and safety.
๐งช Examples for All Key OOP Terms and Types in Ruby
๐งฉ Term / Type | ๐ Example | ๐ก Explanation |
---|---|---|
Class |
| Defines a blueprint for objects (e.g., User objects). |
Object |
| Object created from the class using .new . |
Method |
| Function inside a class defining behavior. |
Instance Variable |
| Stores data unique to each object, prefixed with @ . |
attr_accessor |
| Creates both getter and setter for @name . |
attr_reader |
| Creates read-only access for an attribute. |
attr_writer |
| Allows only write access (used for sensitive data). |
Encapsulation |
| Keeps methods hidden from public use. |
Inheritance |
| Admin inherits methods and attributes from User . |
Polymorphism |
| Same method name behaves differently in child classes. |
Module |
| A collection of methods to be reused in other classes. |
Mixin |
| Mixes module behavior into a class using include . |
self |
| self refers to the current object or class context. |
super |
| Calls the same method in the parent class. |
Duck Typing |
| Focuses on object behavior, not its class type. |
Custom Setter |
| Control attribute assignment with validations or transformations. |
Freeze Attribute |
| Makes the value immutable (cannot be changed). |
Namespace (Module) |
| Organizes code and avoids naming conflicts. |
Class Variable |
| Shared across all instances of the class. Rarely recommended. |
Constant |
| Immutable and typically used for configuration or constants. |
๐งฉ Different Ways to Pass Arguments and Define Parameters in Ruby
๐ง Type | ๐ Syntax | ๐ก Example & Use Case |
---|---|---|
1. Positional Parameters | def greet(name) | Best for required input values. |
2. Default Parameters | def greet(name = \"Guest\") | Allows optional arguments. |
3. Variable Number of Arguments | def sum(*nums) | Accepts any number of arguments. |
4. Keyword Arguments | def login(username:, password:) | Named, required arguments โ improves readability. |
5. Optional Keyword Arguments | def welcome(name: \"Guest\") | Clean way to handle optional values by name. |
6. Keyword Arguments with Double Splat | def configure(**options) | Captures all keyword arguments as a hash. |
7. Mixing Positional + Keyword | def setup(user, admin: false) | Useful for flexible interfaces. |
8. Block Parameters | def each(&block) | Pass a block and call it using yield . |
9. Proc / Lambda Parameters | def process(action) | Pass behavior as an object (OOP style). |
10. Destructured Parameters | def draw((x, y)) | Useful when working with arrays or coordinates. |
๐ง Mastering these parameter types helps you write clean, flexible, and expressive Ruby methods that scale well in real applications and APIs.
โ๏ธ Class Variables vs Instance Variables: Use Cases & Examples
Aspect | Instance Variable @variable | Class Variable @@variable |
---|---|---|
Scope | Belongs to one object (instance) | Shared across all instances and the class |
Used In | Instance methods | Both instance and class methods |
Purpose | Stores data unique to each object | Stores shared data like counters or configs |
Persistence | Exists only for the life of the object | Exists throughout the class lifecycle |
Inheritance | Independent per object | Shared even across subclasses (can be dangerous) |
๐ง Instance Variable Use Cases
- ๐ User profile with
@name
,@email
- ๐ฆ Product with
@price
,@quantity
- ๐ Per-request data in a web app (like session or token)
class Product
def initialize(name, price)
@name = name
@price = price
end
def info
"#{@name} costs $#{@price}"
end
end
p1 = Product.new("Book", 10)
p2 = Product.new("Pen", 2)
puts p1.info # => Book costs $10
puts p2.info # => Pen costs $2
๐ข Class Variable Use Cases
- ๐ข Counter to track total instances
- ๐ฅ Shared configuration value like API limits
- ๐ Aggregated stats across objects
class User
@@user_count = 0
def initialize
@@user_count += 1
end
def self.total_users
@@user_count
end
end
User.new
User.new
puts User.total_users # => 2
โ ๏ธ Best Practice
- โ
Use
@
for per-object state (most common) - โ ๏ธ Be cautious with
@@
โ it’s shared across subclasses and can behave unexpectedly - โ
Prefer
@
with class methods (i.e., class instance variable) if you need shared config
๐ฏ Bonus: Class Instance Variable Example
class Setting
@config = {}
def self.set(key, value)
@config[key] = value
end
def self.get(key)
@config[key]
end
end
Setting.set(:theme, "dark")
puts Setting.get(:theme) # => dark
Class instance variables @var
in class context are safer than @@var
for shared logic, especially in Rails and modern Ruby apps.
๐ Are @
and @@
Variables Internal Only?
โ Yes โ They Are Internal to the Class
In Ruby, both:
@instance_variable
@@class_variable
…are **internal/private to the object or class**. You cannot access them directly from the outside using:
User.variable
โuser.variable
โ
๐ ๏ธ You Need Methods to Expose Them
You can either define your own method or use helpers like:
attr_reader
โ for read-onlyattr_writer
โ for write-onlyattr_accessor
โ for both
๐ Example:
class User
@@total_users = 10
@admin = true
def initialize
@name = "Ali"
end
def name
@name
end
def self.total_users
@@total_users
end
def self.admin
@admin
end
end
puts User.total_users # => 10
puts User.admin # => true
puts User.new.name # => Ali
โ ๏ธ Direct Access (Hacking)
User.class_variable_get(:@@total_users) # => 10
User.instance_variable_get(:@admin) # => true
User.new.instance_variable_get(:@name) # => nil unless initialized
โ But these are not recommended in production apps โ they’re for metaprogramming/debugging.
โ Conclusion
- All
@
and@@
are private/internal by default. - To access them publicly, define getter/setter methods.
- Using
attr_*
makes this easier.
๐ง 20 Technical OOP Questions in Ruby with Answers & Examples
- Q1: What is a class in Ruby?
A: A blueprint for creating objects.class User def greet \"Hello!\" end end
- Q2: How do you create an object?
A: Use.new
method on a class.user = User.new puts user.greet
- Q3: What is an instance variable?
A: A variable tied to a specific object, prefixed with@
.@name = \"Ali\"
- Q4: What does
attr_accessor
do?
A: Creates both getter and setter methods.attr_accessor :name
- Q5: What is encapsulation?
A: Restricting access to internal methods and data.private def secret \"hidden logic\" end
- Q6: How do you inherit from a parent class?
A: Use<
to inherit.class Admin < User end
- Q7: How do you override a method in a subclass?
A: Define the method again in the child class.def greet \"Hi, admin!\" end
- Q8: What is polymorphism in Ruby?
A: Different classes can respond to the same method name.class Dog; def speak; \"Bark\"; end; end class Cat; def speak; \"Meow\"; end; end
- Q9: Whatโs the difference between
include
andextend
?
A:include
adds instance methods;extend
adds class methods. - Q10: How do you define a module?
A: Usemodule
keyword.module Sharable def share puts \"Shared!\" end end
- Q11: What is a mixin?
A: Using a module to add reusable functionality to a class.include Sharable
- Q12: What is
self
in Ruby?
A: Refers to the current object or class context.def self.status \"Active\" end
- Q13: What does
super
do?
A: Calls the same method in the parent class.def initialize(name) super(name) end
- Q14: How do you make an attribute read-only?
A: Useattr_reader
.attr_reader :email
- Q15: How can you validate attribute data in a setter?
A: Define a custom setter method.def age=(val) raise \"Invalid\" if val < 0 @age = val end
- Q16: How do you freeze an attribute value?
A: Use.freeze
to make it immutable.@mode = mode.freeze
- Q17: What is duck typing?
A: Behavior over type โ if it acts like the expected object, itโs accepted.def render(page) page.render_html end
- Q18: How do you define a constant in Ruby?
A: Use all caps. Constants should not change.MAX_RETRIES = 3
- Q19: What is the difference between
@
and@@
?
A:@
is instance-specific;@@
is shared across all instances.@name = \"Ali\" @@count = 0
- Q20: How do you group related classes or modules?
A: Use namespacing with modules.module Admin class Dashboard end end
โ OOP in Ruby โ Best Practices with Examples
- 1. Use
attr_reader
Instead ofattr_accessor
When Mutability Isnโt NeededEncapsulation is key. Prevent accidental changes by exposing only whatโs needed.
class User attr_reader :email def initialize(email) # email is immutable @email = email end end
- 2. Group Shared Methods in Modules (Mixins)
Extract reusable behavior using modules and include them where needed.
module Sharable def share puts \"Shared!\" end end class Post include Sharable end
- 3. Use Custom Setters to Validate Input
Encapsulate rules inside the object and enforce them at the data level.
class Invoice attr_reader :amount def amount=(value) raise \"Invalid amount\" if value <= 0 @amount = value end end
- 4. Keep Classes Small โ Follow SRP (Single Responsibility Principle)
One class should have one purpose. Split logic into separate classes.
# good class OrderProcessor def process(order); end end class PaymentGateway def charge(card, amount); end end
- 5. Prefer Composition Over Inheritance When Behavior Varies
Use helper objects or modules when class hierarchy isnโt clean.
class Notification def initialize(sender) @sender = sender end def deliver(message) @sender.send_message(message) end end
- 6. Use
super
Carefully When Overriding MethodsMake sure to retain base behavior when appropriate.
class Base def greet \"Hello\" end end class Child < Base def greet super + \", friend!\" end end
- 7. Avoid Global State โ Use Dependency Injection
Inject dependencies instead of hardcoding them inside methods.
class Report def initialize(formatter) @formatter = formatter end def generate @formatter.format(data) end end
- 8. Donโt Overuse Inheritance โ Favor Modules for Behavior
Ruby allows mixing modules into multiple classes, unlike single-inheritance limitations.
module Trackable def track puts \"Tracked action\" end end
- 9. Use
initialize
for Required DataForce required attributes up front for a consistent object state.
class Product def initialize(name, price) raise \"Missing info\" unless name && price @name = name @price = price end end
- 10. Freeze Immutable Attributes for Safety
Prevent accidental changes to constant-like values.
@currency = \"USD\".freeze
โจ Following these OOP best practices helps you write clean, scalable, and testable Ruby code โ whether youโre building a small script or a large Rails application.
๐ Instance vs Class vs Static Methods & Variables in Ruby
๐ก Type | ๐ Description | ๐ Syntax & Example | ๐ง Use Case |
---|---|---|---|
Instance Variable@variable | Data unique to each object instance. |
| Used when each object stores its own data. |
Instance Method | Methods that operate on instance variables. |
| Common for business logic tied to a record. |
Class Variable@@variable | Shared across all instances of a class. |
| Used for shared tracking across all instances. |
Class Methodself.method | Methods called on the class itself, not an instance. |
| Used for utility logic or factory methods. |
Constant (Static Variable)CONSTANT | Immutable data shared across all instances. |
| Used for configuration, settings, fixed rules. |
Static-style Method (Module)module.method | Method defined in a module and called without instantiation. |
| Used for utility functions (like helpers). |
โ Summary
- @variable โ Per-object state (instance)
- @@variable โ Shared across instances (class-wide)
- CONSTANT โ Global static-like values
- Instance methods โ Called on objects
- Class methods โ Called on the class
- Static-like methods โ Called via module or class without instantiation
โ Why Do We Use attr_reader
, attr_writer
, attr_accessor
in Ruby?
๐ What are attr_*
methods?
attr_reader
โ generates a **getter**attr_writer
โ generates a **setter**attr_accessor
โ generates both getter and setter
โ Why Use Them?
They reduce **boilerplate code** and improve readability. Instead of manually writing getters and setters, you declare them with one line.
# With attr_accessor
class User
attr_accessor :name
end
# Equivalent to:
class User
def name
@name
end
def name=(value)
@name = value
end
end
๐ค Can We Go Without It?
Yes, technically you can. Ruby lets you define your own getter and setter methods manually:
class Product
def price
@price
end
def price=(value)
@price = value
end
end
โ When You Should NOT Use attr_accessor
- When you want to control or validate input/output.
- When you want read-only access โ use
attr_reader
. - When you’re working with sensitive data like passwords โ use
attr_writer
or custom methods.
๐ง Best Practice Summary
- โ
Use
attr_reader
for immutable or safe-to-read data. - โ
Use
attr_writer
for write-only values (e.g., passwords). - โ
Use
attr_accessor
only if you truly need both read and write access. - ๐ซ Avoid exposing internal state carelessly โ encapsulate when possible.
๐ Example: Custom Setter with Validation
class Account
attr_reader :balance
def balance=(amount)
raise \"Invalid\" if amount < 0
@balance = amount
end
end
๐ In conclusion, while you can manually define getters and setters, using attr_*
saves time, improves readability, and encourages clean design โ just use them wisely!
๐งช Deep Dive: attr_ vs Instance/Class Variables and Method Access in Ruby
- Q1: Can I create instance variables without
attr_*
?
โ Yes.
You can define and use@variable
inside methods without usingattr_reader
,attr_writer
, orattr_accessor
.class User def set_name(name) @name = name end def greet \"Hello, #{@name}\" end end
attr_*
only creates getter/setter methods. You can use instance variables without it if you access them internally. - Q2: Can I call an instance method from a class method?
โ Yes, but you need an object instance.class Demo def greet \"Hello from instance\" end def self.call_greet new.greet # creates an instance and calls greet end end
You canโt directly call an instance method from a class method unless you instantiate the object.
- Q3: Is
attr_*
accessible from any instance method?
โ Yes.
attr_reader
,attr_writer
, andattr_accessor
define methods that can be called from inside other instance methods:class User attr_accessor :name def greeting \"Hi, #{name}\" # same as self.name end end
- Q4: Is
@variable
accessible in all instance methods?
โ Yes.
Once defined,@variable
is available throughout the object’s lifecycle in all instance methods:class Product def set_price(price) @price = price end def show_price \"Price is #{@price}\" end end
- Q5: Can class variables
@@variable
be accessed in instance methods?
โ Yes.
But be cautious โ they are shared across all instances and subclasses.class Counter @@count = 0 def initialize @@count += 1 end def show_count @@count end end
- Q6: Can both class and instance methods/variables work without
attr_*
?
โ Yes.
You can define everything manually.attr_*
is just a helper.class Manual def name @name end def name=(val) @name = val end def self.version @version ||= \"1.0.0\" end end
Summary: You donโt need
attr_*
unless you want to quickly expose instance variables via methods. All instance/class variables and methods can be defined manually and still work.
๐ง Final Tip
Use attr_reader
for safe, readable access, attr_writer
for sensitive inputs, and attr_accessor
for convenience โ but always prefer encapsulation and validation when needed.
โ Can I Access Instance Variables Without attr_*
Using Object or Class Methods?
๐น From an Object? โ Yes
Instance variables (like @name
) are tied to objects. You can access them in any instance method โ and call that method on the object โ even without attr_reader
.
class User
def initialize(name)
@name = name
end
def greet
"Hi, #{@name}"
end
end
user = User.new("Ali")
puts user.greet # => "Hi, Ali"
๐น From a Class Method? โ No
Class methods do NOT have access to instance variables like @name
, unless they create an instance:
class User
def initialize(name)
@name = name
end
def greet
"Hi, #{@name}"
end
def self.say_hi
user = new("Admin")
user.greet # This works because we created an instance
end
end
puts User.say_hi # => "Hi, Admin"
โ ๏ธ Important Notes
- ๐ก attr_* is just syntactic sugar for defining getter/setter methods โ itโs not required to use or access instance variables internally.
- ๐ซ You can’t do
user.@name
directly โ Ruby does not allow accessing instance variables from outside without a getter method. - โ
You can use
instance_variable_get
(not recommended for production):user.instance_variable_get(:@name)
โ Conclusion
You can use and access instance variables without attr_*
, but only inside instance methods. If you want to access them from outside the object, you need to define a method (manually or via attr_reader
).
๐ง Can attr_*
Work Without Defining @variable
?
๐ Explanation
When you use attr_accessor :name
, Ruby automatically creates:
- A getter method:
def name; @name; end
- A setter method:
def name=(value); @name = value; end
So, even if you never manually define @name
, it will be created the moment you assign a value using self.name = "Ali"
or object.name = "Ali"
.
โ
Example Without Explicit @name
class Person
attr_accessor :name
def greet
"Hello, #{name}"
end
end
p = Person.new
p.name = "Ali"
puts p.greet # => "Hello, Ali"
๐ Even though @name
is never explicitly defined in the class, the moment you run p.name = "Ali"
, Ruby internally sets @name = "Ali"
.
๐ Key Point
attr_*
creates instance methods that read from or write to the corresponding @variable
. Itโs safe to use in your class without defining the variable yourself.
๐ Reminder
If you try to access name
before setting it, you’ll get nil
, because the instance variable @name
hasn’t been set yet.
p2 = Person.new
puts p2.name # => nil
๐ Summary
- โ
You donโt need to write
@name
manually if you useattr_accessor
. - โ
Ruby handles the creation of
@name
when the setter is called. - โ
You can access
name
inside any instance method using the getter methodname
.
๐ Real-World Case Studies: OOP in Ruby & Rails (10 Scenarios)
- ๐ E-commerce Cart (Encapsulation)
Problem: Cart logic (add/remove items, total price) scattered in controllers.
OOP Solution: Encapsulate cart logic in aCart
class.class Cart def initialize @items = [] end def add(product) @items << product end def total @items.sum(&:price) end end
- ๐ค User Authentication (Inheritance)
Problem: Admin and Customer share login logic but differ in permissions.
OOP Solution: Inherit from a commonUser
base class.class User def login; end end class Admin < User def dashboard; end end
- ๐จ Notification System (Polymorphism)
Problem: Need to send notifications via email, SMS, and push.
OOP Solution: Create polymorphicNotifier
interface.class EmailNotifier def send; puts \"Email\"; end end class SmsNotifier def send; puts \"SMS\"; end end
- ๐ฆ Order Fulfillment (Service Object)
Problem: Checkout logic is large and messy.
OOP Solution: Move logic into a class with clear responsibility.class CheckoutService def initialize(order) @order = order end def call charge_payment update_inventory end end
- ๐ Dashboard Metrics (Presenter Pattern)
Problem: Complex formatting logic inside views.
OOP Solution: Add a presenter class to format data.class MetricPresenter def initialize(metric) @metric = metric end def display \"#{@metric.name}: #{@metric.value.round(2)}\" end end
- ๐ Invoice Generation (Composition)
Problem: PDF generation and tax calculation are coupled.
OOP Solution: ComposeInvoice
class withTaxCalculator
andPdfGenerator
.class Invoice def initialize(tax_calc, pdf) @tax_calc = tax_calc @pdf = pdf end def generate total = @tax_calc.compute @pdf.render(total) end end
- ๐ Role-Based Access Control (Modules & Mixins)
Problem: Common permission checks repeated in many models.
OOP Solution: UsePermissions
module as mixin.module Permissions def can_edit? role == 'admin' end end
- ๐ง Email Formatting (Decorator Pattern)
Problem: Email content is hard to format dynamically.
OOP Solution: Wrap email object with decorators.class EmailDecorator def initialize(email) @email = email end def formatted_subject \"[#{Time.now.year}] #{@email.subject}\" end end
- ๐ Form Handling (Form Object)
Problem: Multiple models handled in one form (e.g., user + profile).
OOP Solution: Create a PORO Form object to validate/submit both.class SignupForm include ActiveModel::Model attr_accessor :user, :profile def save user.save && profile.save end end
- ๐งพ Payment Gateway Abstraction (Interface Pattern)
Problem: Switch between Stripe, PayPal, etc., without rewriting code.
OOP Solution: Create shared interface for all gateways.class StripeGateway def charge(amount) # Stripe logic end end class PaypalGateway def charge(amount) # PayPal logic end end
๐ง These scenarios show how OOP isn't just theory โ it's used in almost every part of a real-world Ruby or Rails application to improve code clarity, reuse, and maintainability.
Learn more aboutย Railsย setup
https://shorturl.fm/j3kEj
https://shorturl.fm/9fnIC
https://shorturl.fm/a0B2m
https://shorturl.fm/TDuGJ
https://shorturl.fm/fSv4z
https://shorturl.fm/xlGWd
https://shorturl.fm/PFOiP
https://shorturl.fm/PFOiP
https://shorturl.fm/PFOiP
https://shorturl.fm/47rLb