# rails-settings-cached: a global key value store for Rails settings

> rails-settings-cached turns a database table into a typed settings store for a Rails application, where every key is declared as a field with a type, a default and optionally a validation, and read through class methods that cache the values. It suits a Rails app that needs admin-editable configuration at runtime, and it requires Ruby 3.2 and Rails 7.0 or newer on the current line.

**huacnlee/rails-settings-cached** — Global settings for your Rails application.

- Repository: https://github.com/huacnlee/rails-settings-cached
- Stars: 1,124 · Forks: 205
- Language: Ruby
- License: NOASSERTION
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/huacnlee-rails-settings-cached

## A global Hash that lives in your database

rails-settings-cached calls itself the best solution for storing global settings in Rails applications, and the analogy it uses is the clearest description of the design. Think of it as a global Hash stored in your database, using simple ActiveRecord-like methods for manipulation, so you can keep track of any global setting you would rather not hard code into your Rails application. That framing is accurate in one important sense, since the values are stored as rows and read through class methods, but it understates the rest of the gem, because the settings are declared, typed and validated rather than being a free-form key value bag. The primary language is Ruby, the licence is not one GitHub can classify and the file in the repository is `MIT-LICENSE`, so the MIT terms are there but the metadata does not say so. The last push was on 2026-09-16 and the release was v2.10.0 on the same day, with v2.9.6 from 2024-12-10 before it.

## Installing, and generating the settings model

The current line requires Ruby 3.2 and Rails 7.0 or newer, and for older Ruby or Rails versions the README points at the 2.9.x releases. The install is a bundle command:

```bash
$ bundle add rails-settings-cached
```

Then a generator creates the model, and it can take a custom class name:

```bash
$ rails g settings:install

# Or use a custom name:
$ rails g settings:install AppConfig
```

You end up with `app/models/setting.rb` containing a class that inherits from `RailsSettings::Base`, and the migration that goes with it. The README then tells you to put that migration into the database with `rails db:migrate`, which is the step that creates the table. The generator approach is the right one here because the field declarations are the schema, and having the model and the migration generated together means the two cannot drift.

## Fields, scopes, types and defaults

The generated model is where you declare everything, and the README's example is a good tour. A field can have a default, a validation, a readonly flag, a type, an option list and a help text:

```rb
class Setting < RailsSettings::Base
  # cache_prefix { "v1" }

  scope :application do
    field :app_name, default: "Rails Settings", validates: { presence: true, length: { in: 2..20 } }
    field :host, default: "http://example.com", readonly: true
    field :default_locale, default: "zh-CN", validates: { presence: true, inclusion: { in: %w[zh-CN en jp] } }, option_values: %w[en zh-CN jp], help_text: "Bla bla ..."
    field :admin_emails, type: :array, default: %w[admin@rubyonrails.org]
  end

  scope :limits do
    field :user_limits, type: :integer, default: 20
    field :exchange_rate, type: :float, default: 0.123
    field :captcha_enable, type: :boolean, default: true
  end

  field :notification_options, type: :hash, default: {
    send_all: true,
    logging: true,
    sender_email: "foo@bar.com"
  }
end
```

Two rules matter. You must use the `field` method to declare the setting keys, otherwise you cannot use it, so an undeclared key is not a runtime error but an absent key. And the `scope` method groups the keys for an admin UI, so the scope is not cosmetic, it is the grouping an administrative interface will render. The commented-out `cache_prefix { "v1" }` line is the knob for cache invalidation across deploys, which is worth noticing in a gem whose name ends in cached.

## Coercion happens on assignment, not on read

The behaviour that makes the gem pleasant is that assignment coerces the value to the declared type, so you never parse strings yourself. The README's irb session shows it. Assigning the string "30" to an integer field gives back 30. Assigning "0", "1", "false" or "true" to a boolean field gives back false, true, false and true respectively, and there is a `?` form that returns a strict boolean, so `Setting.captcha_enable?` is true where `Setting.captcha_enable` is the raw value. An array field accepts a comma or newline separated string and splits it, so setting `admin_emails` to a string with commas and a newline produces an array of the three addresses. And a hash field accepts a hash literal, with the caveat that assigning a partial hash replaces the whole value rather than merging into it, since the example shows the `send_all` and `logging` keys disappearing when only `sender_email` is assigned. That last one is a genuine surprise if you expected a deep merge.

## Validations run on save and raise on failure

Fields accept Rails validations, using the standard ActiveModel::Validations class methods, and they run when the record is saved. The example shows a presence and length validation on `app_name` and an inclusion validation on `default_locale`. Assigning an empty string to the app name raises `ActiveRecord::RecordInvalid` with a message that the app name can't be blank, and assigning a locale outside the allowed list raises the same error with a message naming the allowed values. You can also validate explicitly through the `save` or `valid?` methods, and the README shows the pattern of locating a specific row with `Setting.find_or_initialize_by(var: :app_name)`. The consequence is that a bad value is refused at the point of assignment rather than being stored and discovered later, which is the behaviour you want when the writer is an admin form.

## Introspection for building an admin interface

The gem exposes its own metadata, which is what you need to build a settings screen without hardcoding a field list. `Setting.keys` returns all keys, `Setting.editable_keys` returns the ones that are not readonly, and `Setting.readonly_keys` returns the ones that are. `Setting.get_field` returns the full field definition as a hash, with the scope, key, type, default and readonly flag, and it accepts either a string or a symbol, so `Setting.get_field(:user_limits)` works as well as `Setting.get_field('host')`. The options and help text are reachable through the same hash, as `Setting.get_field("default_locale")[:options]`. Since version 2.7.0 there is also `defined_fields`, which returns the field definitions so you can select the editable ones and group them by scope, which is exactly the shape an admin form needs.

## Custom field types, and the version that added them

Since version 2.9.0 you can define your own field type under the `RailsSettings::Fields` module, subclassing `RailsSettings::Fields::Base` and implementing `serialize` and `deserialize`. The example defines a `YesNo` type that stores the string "YES" or "NO" and raises a StandardError for anything else, and deserializes "YES" to true, "NO" to false and anything unexpected to nil. Once the type exists you use it by name:

```rb
class Setting
  field :custom_item, type: :yes_no, default: 'YES'
end
```

and the field then behaves like a boolean, with `Setting.custom_item` returning true or false. The practical value is that a bespoke type keeps the storage representation out of your application code, so a setting that has to be stored as "YES" for an external system does not spread that string through the codebase. The `else raise StandardError` in the example is a deliberate choice worth copying, since a silent fallback would let a bad value look like a valid one.

## Conclusion

Adopt rails-settings-cached if you need settings that an administrator can change without a deploy, and you want them typed and validated rather than a bag of string keys. Skip it if your configuration is static and belongs in the environment, since a database row is a worse home for a value that only changes on deploy. Before you start, note the version floor, Ruby 3.2 and Rails 7.0 for the 2.10 line with the 2.9.x releases for older versions, and remember that a key only works if it was declared with `field`, since undeclared keys are not available through the typed API.

## FAQ

### How do I install and set up rails-settings-cached?

Run `bundle add rails-settings-cached`, then generate the model with `rails g settings:install` or `rails g settings:install AppConfig` for a custom name, and run `rails db:migrate` to create the table. The current line requires Ruby 3.2+ and Rails 7.0+; the 2.9.x releases are for older versions.

### What types can a rails-settings-cached field have?

The built-in types include string, array, integer, float, boolean and hash, and since version 2.9.0 you can define your own type under the RailsSettings::Fields module by subclassing RailsSettings::Fields::Base and implementing serialize and deserialize.

### How do validations work in rails-settings-cached?

Fields accept standard Rails validation options in a validates key, such as presence, length and inclusion. They run on record save, and an invalid assignment raises ActiveRecord::RecordInvalid. You can also check explicitly with the save or valid? methods.

### How do I get the list of settings defined in rails-settings-cached?

Setting.keys returns all keys, editable_keys and readonly_keys split them, get_field returns a field's full definition, and since version 2.7.0 defined_fields returns the field definitions so you can select the editable ones and group them by scope.

## Sources

- [huacnlee/rails-settings-cached on GitHub](https://github.com/huacnlee/rails-settings-cached)
- [Issues](https://github.com/huacnlee/rails-settings-cached/issues)
- [README](https://github.com/huacnlee/rails-settings-cached/blob/main/README.md)
- [Releases](https://github.com/huacnlee/rails-settings-cached/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/huacnlee-rails-settings-cached
