JoshReedSchramm
JoshReedSchramm

Reputation: 2761

Modeling overridable settings in Rails

I'm trying to figure out the best way to model something and haven't found much yet to help.

My app has a sort of reporting hierarchy. Users are a part of an organization which is part of an account.

I'm trying to figure out the right way to model "Settings". So a given account can default values for settings for all accounts under then. Those defaults can be overridden at the organization level and the user can further override them.

Do far I've handled this by just mapping each record to the appropriate parent record (user, account, org) but on a query there's a lot of work to combine them all and get the final definitive settings list.

Any thoughts on the right way to handle this?

Upvotes: 0

Views: 62

Answers (1)

Pavel Mikhailyuk
Pavel Mikhailyuk

Reputation: 2877

My key thoughts:

  • Represent Settings as tree based on Ruby's Hash
  • Store Settings with JSON like DB type(jsonb as PostgreSQL example)
  • Use Active Support's Hash#deep_merge to override Settings

Example:

class User < ApplicationRecord
  belongs_to :organization
  # settings | jsonb | not null default '{}'::jsonb
  # ...

class Organization < ApplicationRecord
  belongs_to :account
  # settings | jsonb | not null default '{}'::jsonb
  # ...

class Account < ApplicationRecord
  # settings | jsonb | not null default '{}'::jsonb
  # ...

Then

class Settings
  cattr_reader :global_settings do
    # hash = ... read global settings somehow
    hash.freeze # prevent global settings from changes
  end

  def self.settings_for(user_id)
    User
      .joins(organization: :account)
      .where(id: user_id)
      .limit(1)
      .pluck(*%w[ accounts.settings organizations.settings users.settings ])
      .first
      .reduce(global_settings){ |memo, e| memo.deep_merge(e) }
      # or
      # .each_with_object(global_settings.dup){ |e, memo| memo.deep_merge!(e) }
  end
  # ...

Settings.settings_for(111)

It costs one fast DB query( deep_merge << DB query), front-end likes to deal with JSON. So it will work. At least for start.

Upvotes: 1

Related Questions