Ruby
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Methods and Blocks
-
Section 4: Object-Oriented Ruby
-
Section 5: Metaprogramming
-
Section 6: Working with Data and Files
-
Section 7: Ruby Frameworks Overview
-
Section 8: Ecosystem and Testing
-
Section 9: Practical Projects
-
Section 10: Interview Practice
-
Section 11: Data Structures and Algorithms in Ruby
-
Section 12: More Practice Exercises
-
Section 13: Enumerable and Functional Style
-
Section 14: More OOP Practice
-
Section 15: More Testing
-
Section 16: Enumerable and Comparable Modules In Depth
-
Section 17: Ruby Standard Library: Core Utilities
-
Section 18: Ruby Standard Library: Data and Security
-
Section 19: Ruby Standard Library: CLI and Text
-
Section 20: Ruby Networking
-
Section 21: Ruby on Rails Deep Dive
-
Section 22: Ruby Metaprogramming Deep Dive
-
Section 23: Ruby Design Patterns
-
Section 24: Ruby Concurrency
-
Section 25: Ruby Testing Deep Dive
-
Section 26: Ruby Gems and Packaging
-
Section 27: Ruby Performance
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Rails API Development
-
Section 31: Rails Authentication and Authorization
-
Section 32: Rails Testing Deep Dive
-
Section 33: Rails Performance
-
Section 34: Rails Deployment
-
Section 35: Sinatra and Lightweight Ruby Web Apps
-
Section 36: More Ruby Language Deep Dive
-
Section 37: Ruby 3.x Modern Features
-
Section 38: More Data Structures in Ruby
-
Section 39: More Practical Projects
-
Section 40: Ruby Ecosystem Tools
-
Section 41: More Practice and Review
-
Section 42: Final Practice and Mastery
-
Section 43: Ruby Interview Deep Dive
-
Section 44: Ruby Background Processing Deep Dive
-
Section 45: Ruby GraphQL
-
Section 46: Ruby Object Model Deep Dive
-
Section 47: Ruby Hanami Framework Overview
-
Section 48: Ruby gRPC and Protocol Buffers
-
Section 49: Ruby Data Processing
-
Section 50: Ruby Search Integration
-
Section 51: Ruby File Upload and Media
-
Section 52: Ruby Email and Notifications
-
Section 53: Ruby Admin Panels
-
Section 54: Ruby Feature Flags and Experimentation
-
Section 55: Ruby Monitoring and Observability
-
Section 56: Ruby Docker and Deployment Deep Dive
-
Section 57: Ruby Security Deep Dive
-
Section 58: More Advanced Metaprogramming
-
Section 59: More Final Projects
246: Building a Simple CRM Backend
When people first hear "CRM backend," they usually panic and think they need to set up a PostgreSQL database, configure a complex Rails migration, and build a REST API before they can even track a single customer. I see this all the time. There is a persistent belief that the "CRM" part of the name implies a specific infrastructure rather than a specific logic.
The "Database First" Trap vs. Domain Modeling
If you start by worrying about tables and foreign keys, you'll likely over-engineer your data structure before you even understand how your specific business process works. You don't need a database to build a CRM; you need a way to model the relationship between a person, their company, and the history of your conversations with them.
Look at this approach. Instead of thinking about "Rows in a table," let's think about "Objects in Ruby." If we are building a CRM for a freelance consultant, the core entity isn't a "record"—it's a Contact. A Contact has a name, an email, and a list of Interactions. By building this in plain Ruby first, we can iterate on the logic in seconds rather than waiting for migrations to run.
class Interaction
attr_reader :date, :note
def initialize(date, note)
@date = date
@note = note
end
end
class Contact
attr_reader :name, :email, :interactions
def initialize(name, email)
@name = name
@email = email
@interactions = []
end
def add_interaction(note)
# I like using Time.now here so the user doesn't have to manually input dates
@interactions << Interaction.new(Time.now, note)
end
end
From Individual Objects to a Managed System
Now, having a Contact class is great, but a CRM isn't just a collection of people; it's a system for finding and managing those people. You don't want to be manually managing an array of objects in your main script. You need a coordinator—a manager class that handles the "registry" of your clients.
I usually implement this using a Hash. Why? Because looking up a contact by a unique ID in a Hash is significantly faster than iterating through an Array of objects to find a matching name. It's a small detail that makes a huge difference as your "database" grows from ten people to a thousand.
class CRM
def initialize
@contacts = {}
@next_id = 1
end
def add_contact(name, email)
contact = Contact.new(name, email)
id = @next_id
@contacts[id] = contact
@next_id += 1
id
end
def find_contact(id)
@contacts[id] || raise("Contact with ID #{id} not found!")
end
def list_all_contacts
@contacts.map { |id, contact| "#{id}: #{contact.name} (<#{contact.email}>)" }
end
end
# Quick test drive:
my_crm = CRM.new
client_id = my_crm.add_contact("Alice Smith", "alice@example.com")
my_crm.find_contact(client_id).add_interaction("Discussed Q3 pricing")
puts my_crm.list_all_contacts
Notice how the CRM class encapsulates the storage. The rest of your application doesn't need to know that you're using a Hash internally. If you decide later that you want to save this to a JSON file or move it to a real SQL database, you only have to change the code inside the CRM class. The way you call add_contact or find_contact stays exactly the same. That's the power of decoupling your business logic from your storage mechanism.
📋 Practical Task
Implementing a Lead Priority Scoring System
In a real CRM, not all contacts are created equal. Some are "hot leads" and some are just "cold" contacts. Your task is to extend the CRM backend we just built to include a priority scoring system.
Modify the Contact class and the CRM class to implement the following:
- Add a
priority_scoreattribute to theContactclass (defaulting to 0). - Add a method
Contact#update_score(points)that adds or subtracts points from the priority score. - Add a method
CRM#high_priority_contactsthat returns an array of all contacts with a score of 10 or higher.
Testing your work: Create a CRM instance, add three different contacts, give two of them scores above 10, and verify that high_priority_contacts only returns those two.
There are no comments for now.