We have all been there. You are five minutes into a dinner reservation when your phone buzzes. The marketing team wants to enable the "Holiday Sale Banner" right now, or an external API is down and you need to switch the app into a partial maintenance mode.
If your configuration is hardcoded in Ruby files or locked inside Environment Variables, making that change requires a commit, a build, and a deploy. That’s a 15-minute process for a 1-second change.
There is a better way. By leveraging ActiveRecord, we can create a lightweight, dynamic Key-Value store right inside your application database.
Here is how to implement a dictionary-style System Setting interface in Rails.
The Setup
We don't need a heavy gem like rails-settings-cached for simple use cases. We just need a table to hold keys and values.
1. The Migration
First, let's create a table to store our settings. We use text for the value to allow for long strings (like announcement messages) and ensure the key is unique so we don't get duplicates.
# db/migrate/20260115225500_create_system_settings.rb
class CreateSystemSettings < ActiveRecord::Migration[8.0]
def change
create_table :system_settings do |t|
t.string :key, null: false
t.text :value
t.timestamps
end
add_index :system_settings, :key, unique: true
end
end
2. The Model (With Syntax Sugar)
This is where the magic happens. A standard Rails model would force you to write verbose queries like SystemSetting.find_by(key: 'promo'). We can do better.
By defining self.[] and self.[]= class methods, we can treat our database table like a standard Ruby Hash.
# app/models/system_setting.rb
class SystemSetting < ApplicationRecord
validates :key, presence: true, uniqueness: true
# The Getter
# Usage: SystemSetting[:maintenance_mode]
def self.[](key)
find_by(key: key)&.value
end
# The Setter
# Usage: SystemSetting[:maintenance_mode] = "on"
def self.[]=(key, value)
setting = find_or_initialize_by(key: key)
setting.value = value
setting.save!
end
end
How to Use It
Because we mapped the array accessors, the API is incredibly clean. You can use it anywhere in your controllers, views, or background jobs.
Setting a value (Console or Admin Panel):
SystemSetting[:global_announcement] = "Maintenance scheduled for 10 PM EST."
Reading a value (In your Application Layout):
<% if SystemSetting[:global_announcement].present? %>
<div class="alert alert-warning">
<%= SystemSetting[:global_announcement] %>
</div>
<% end %>
Real-World Use Cases
1. The "Kill Switch"
Third-party integrations fail. If your SMS provider goes down, you don't want your background workers retrying thousands of times and crashing your Redis instance.
def send_sms
return if SystemSetting[:sms_enabled] == "false"
# ... execute sending logic
end
2. Dynamic Feature Flags
Want to let a specific feature go live at a specific time without a deploy?
if SystemSetting[:beta_features_active] == "true"
render :new_dashboard
else
render :old_dashboard
end
Leveling Up: Adding Caching
The implementation above is great, but it has one flaw: it hits the database every time you ask for a setting. If you put this in your application header, you are adding a SQL query to every single page load.
Since these settings rarely change, they are perfect candidates for Rails.cache. Here is the production-ready version:
class SystemSetting < ApplicationRecord
validates :key, presence: true, uniqueness: true
after_commit :clear_cache
def self.[](key)
Rails.cache.fetch("system_setting:#{key}") do
find_by(key: key)&.value
end
end
def self.[]=(key, value)
setting = find_or_initialize_by(key: key)
setting.value = value
setting.save!
end
private
def clear_cache
Rails.cache.delete("system_setting:#{key}")
end
end
Now, the database is only queried once. Subsequent requests pull instantly from memory (Redis/Memcached), and the cache is automatically busted whenever you update the record.
Summary
You don't always need complex infrastructure for configuration. Sometimes, a simple database table and a little bit of Ruby syntax sugar are all you need to make your application more flexible and your life easier.
Deploy the code once. Change the settings whenever you want.
Top comments (0)