Rails + Storing User Settings — 2016 Edition

I wrote about this problem back in 2013. And conveniently, tech has gotten much better since then. So, it is time for an update 😀.

JSONB + Storext is currently my favorite method for storing user settings in a Rails app. It’s super flexible, like NoSQL, but behaves exactly like the standard Rails attributes that you already know and love.

(if you want to know more about why I think this is best, skip to the end, I explain there)

How to do it

What you’ll need

  • Postgres 9.4+ (is possible with lower versions, if you use hstore)
  • Rails 4.x+

1. Add the column

First, you’ll need to add a new jsonb column to your users table. This is where you’ll store all of your user’s settings. We use JSON so that we can fit all of our settings in a single column.

# db/migrate/xxx_add_settings_to_user.rb
class AddSettingsToUser < ActiveRecord::Migration
def change
add_column :users, :settings, :jsonb, null: false, default: ‘{}’

Alternative column naming ideas: preferences, notification_settings, email_settings

Remember to run the migration. rake db:migrate

2. Install Storext

Your settings are stored as json in Postgres, but we want them to act like regular ActiveRecord columns. Storext sits on top of ActiveRecord::Store and adds some nice extra features (such as type coercion) to your json values.

Add gem 'storext' in your Gemfile and run bundle install.

3. Add your settings

Now the fun part. Defining your settings. These are done with Storext’s store_attributes method.

class User < ActiveRecord::Base
include Storext.model
# "settings" matches what we named the database column
store_attributes :settings do
subscribed_to_newsletter Boolean, default: false
time_zone String
theme String, default: 'dark'
send_highlights_browser_push Boolean, default: true
send_mention_email Boolean, default: true

All of your standard Rails validations (except for uniqueness) will work.

4. You’re done!

You’ll now be able to use your new settings just like you would any other ActiveRecord attribute. 🌟

More info

I love this solution because it’s super flexible. You can add and remove settings easily without tedious (or even downtime) migrations. They are also indexable and queryable if needed.

When I stumbled upon Storext, I got really excited. It gives us some really nice features, such as default values and type coercion. Coercion comes in incredibly useful for managing user settings. If a Rails form submits the value “1” for a checkbox instead of a true, Storext will store it as a boolean as expected. This behavior matches what Rails does internally when setting normal attributes.

Why not a separate table?

User settings tend to become a core piece of every application. If they are retrieved on every request, you’re now making an extra query with each request. Using a separate table makes sense when you have to have individual columns for each setting. But since we’re using jsonb, that’s not an issue. It’s just a single column.

Why jsonb instead of hstore?

Hstore is only for storing text. If you’re going to store boolean or integer values, you start having to coerce the values on read. This works, but is a little messy. The boolean/integer value you see in Rails is different than what’s stored in the database. If you connect this database to a different application that’s missing that application logic… well, you have to write the coercion again in that other application.

Note: If you’re going to be querying off of your settings values, be sure to add a gin index.

For more information, I recommend reading “Hstore vs. JSON — Which to Use in Postgres” by Craig Kerstiens.

If you found this helpful, you can 💥join my email list here💥. I send out an email each time I publish something newIf you have a question, respond to this post or reach out on Twitter @mscccc.




code @ProductHunt. my little sister thinks i’m cool.

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Mike Coutermarsh

Mike Coutermarsh

code @ProductHunt. my little sister thinks i’m cool.

More from Medium

Using esbuild with rails 7 in a simple way

Quick Guide to Nested Attributes in Rails

Ruby on Rails Console

Sporum: A Ruby on Rails, React project