Building Scentfolio: A Full-Stack Fragrance Tracking Platform with Ruby on Rails
Former accountant turned tech student, currently learning to code. This is my public journal as I explore the world of tech- documenting lessons, struggles and small victories. No expertise, just curiosity, consistency, and honest progress.
The Problem
It was time to put all I have learnt throughout my time at Le Wagon to the test. Hearing about how to build a full stack website was sounding easier than ever. However, I wanted to put myself to the test to build something outside of the construct of Le Wagon. No challenges that I had to follow broken down by steps nor being spoon fed what the code would be to decipher whether I pass a challenge.
I chose a niche that I find exciting: fragrances. Throughout the years I have been using websites hanging on by their last html line of code to gather my knowledge about fragrances. This interest came randomly but intensely. I was curious about the notes, the perfumer, and mostly excited to share my reviews on said fragrances.
However, I found the websites visually unappealing and underwhelming So for my first website, I wanted to collate and put to the test if I could create a full stack website for fragrance lovers such as myself, that was actually interesting to navigate. In turn, I would ultimately end up using what I have learned to create such.
Fragrance lovers often own multiple perfumes, sample new scents regularly, and develop preferences that evolve over time as your nose changes as we call it. Yet most people rely on memory, notes apps, or spreadsheets to track their fragrance journey. I wanted a better solution. That's why I built Scentfolio, a web application designed to help users track, discover, and cherish their fragrance experiences in one place.
From Idea to Application
The original vision was simple: create a digital fragrance diary web app, where users could record the scents they own and their impressions of each fragrance.
As development progressed, the project evolved into a full-stack web application featuring user accounts, fragrance collections, personal scent entries and analytics dashboards.
Technology Stack - Ruby on Rails
In Le Wagon we used Ruby on Rails because it provides a robust framework for rapidly building database-driven applications. Rails allowed me to focus on solving user problems instead of spending excessive time on configuration.
Database Design
The website app stores fragrance entries and user data in a structured database, enabling users to create fragrance entries, update scent notes, tack personal ratings and maintain a fragrance history over time.
Using Active Record made managing relationships and data persistence straightforward. Authentication and personalisation A fragrance journal is personal. To support this, I implemented user registration and login functionality, allowing users to securely manage their own collections and entries.
The User model uses Rails' built-in authentication framework.
has_secure_password
Email addresses are normalized before storage:
normalizes :email,
with: ->(e) { e.strip.downcase }
This prevents duplicate accounts. Additional validations were used to ensure unique email addresses, minimum password length and proper email formatting.
Once the user is signed in, they are able to create fragrance diary, edit existing diary entries, view the analytics and maintain a unique fragrance hsitory. I ensured that the user data and fragrance data would remain separate.
The central model in the web app is the Entry model. Each entry represents a fragrance experience rather than simply a perfume bottle. This design allows users to record details such as fragrance name, brand, notes, personal rating, date worn, longevity and personal notes.
class Entry < ApplicationRecord
OCCASIONS = [ "Work", "Evening Out", "Special Event", "Casual", "Date Night", "Sport", "Travel" ].freeze
validates :fragrance_name, presence: true,
length: { maximum: 100 },
format: { without: /<[^>]+>/, message: "must not contain HTML" }
validates :brand, length: { maximum: 100 },
format: { without: /<[^>]+>/, message: "must not contain HTML" },
allow_blank: true
validates :notes, length: { maximum: 1000 },
format: { without: /<[^>]+>/, message: "must not contain HTML" },
allow_blank: true
validates :occasion, inclusion: { in: OCCASIONS, message: "is not a valid occasion" },
allow_blank: true
validates :strength, presence: true, inclusion: { in: 1..10 }
validates :worn_on, presence: true
scope :recent, -> { order(worn_on: :desc) }
scope :this_week, -> { where(worn_on: 1.week.ago..) }
def self.favorite_occasion
where.not(occasion: [ nil, "" ])
.group(:occasion)
.order("count_all DESC")
.count
.first&.first
end
def self.avg_strength
average(:strength)&.round(1)
end
end
Treating entries as experiences rather than products creates a richer dataset and allows users to build a true fragrance journey overtime. The relationship structure is intentionally simple:
User has_many: entries
Entry belongs _to user
This means the user can maintain hundreds of fragrance entries which preserving clear ownership and data integrity.
Data Validation
One of my priorities during development was ensuring that the database only stores meaningful and clean data.
Rails validations are used extensively throughout the Entry model. I made sure that data validations would be necessary when inputting the fragrance name, strength rating must fall between 1 and 10, dates the fragrance was worn would be mandatory, text fields would have length restrictions and finally HTML input would be blocked to rpecent malicious content and maintain data integrity.
validates :strength,
presence: true,
inclusion: { in: 1..10 }
To improve data consistency, fragrance occasions were restricted to a predefined list and to avoid duplicates. Rather than repeatedly writing database queries throughout the application, I used Active Record scopes to encapsulate common filters. This keeps controllers clean while making the code easier to maintain.
The web app followed Rails' RESTful conventions through the Entries Controller.
class EntriesController < ApplicationController
def index
@entries = Entry.order(worn_on: :desc)
end
def show
end
def new
@entry = Entry.new(worn_on: Date.today)
end
def create
@entry = Entry.new(entry_params)
if @entry.save
redirect_to root_path, notice: "Entry added successfully!"
else
render :new, status: :unprocessable_entity
end
end
def edit
end
def update
if @entry.update(entry_params)
redirect_to entries_path, notice: "Entry updated!"
else
render :edit, status: :unprocessable_entity
end
end
def destroy
@entry.destroy
redirect_to entries_path, notice: "Entry deleted."
end
For routes, I used clean RESTful routing
: resources :entries
which automatically generated routes for index, show, new, create, edit, update and delete. Then I added custom routes for the analytics dashboard, user registration and user login.
Rails.application.routes.draw do
root "pages#home"
get "analytics", to: "pages#analytics", as: :analytics
resources :entries
get "sign-up", to: "sessions#new_signup", as: :sign_up
post "sign-up", to: "users#create"
get "log-in", to: "sessions#new_login", as: :log_in
end
Key feature was the fragrance collection management
Users can maintain a digital inventory of their fragrances, making it easy to organise and revisit their collection. One of the most interesting aspects of the project is the analytics section. Instead of simply storing fragrance data, the application helps users understand patterns in their preferences and usage habits.
Challenges Along the Way
Building a full-stack application introduced challenges beyond front-end development. Displaying fragrance data is easy. Turning that data into useful insights for users required additional thought around dashboard design. Because the data is stored in structured tables rather than unstructured notes, these insights can be generated efficiently through database queries.
What I Learned
This project helped me strengthen my understanding of Ruby on rails architecture, MVC design patterns, database design, authentication systems and full-stack application development. Most importantly, it reinforced the value of building software around a genuine passion.
Final Thoughts
Scentfolio began as a simple idea and grew into a full-stack web platform that combines technology with a personal passion for fragrance.
The project continues to evolve, but it has already become one of the most rewarding applications I've built because it solves a real problem for a community I care about.
Building software is always more enjoyable when it's connected to something meaningful, and for me, fragrance was the perfect place to start.