• Stars
    star
    1,005
  • Rank 45,350 (Top 0.9 %)
  • Language
    Ruby
  • License
    MIT License
  • Created over 13 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Global settings for your Rails application.

Rails Settings Cached

The best solution for store global settings in Rails applications.

This gem will make managing a table of а global key, value pairs easy. Think of it like a global Hash stored in your database, that uses simple ActiveRecord like methods for manipulation. Keep track of any global setting that you don't want to hard code into your Rails application.

Gem Version build

Installation

Edit your Gemfile:

$ bundle add rails-settings-cached

Generate your settings:

$ rails g settings:install

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

You will get app/models/setting.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[[email protected]]

    # lambda default value
    field :welcome_message, type: :string, default: -> { "welcome to #{self.app_name}" }, validates: { length: { maximum: 255 } }
    # Override array separator, default: /[\n,]/ split with \n or comma.
    field :tips, type: :array, separator: /[\n]+/
  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: "[email protected]"
  }

  field :readonly_item, type: :integer, default: 100, readonly: true
end

You must use the field method to statement the setting keys, otherwise you can't use it.

The scope method allows you to group the keys for admin UI.

Now just put that migration in the database with:

$ rails db:migrate

Usage

The syntax is easy. First, let's create some settings to keep track of:

irb > Setting.host
"http://example.com"
irb > Setting.app_name
"Rails Settings"
irb > Setting.app_name = "Rails Settings Cached"
irb > Setting.app_name
"Rails Settings Cached"

irb > Setting.user_limits
20
irb > Setting.user_limits = "30"
irb > Setting.user_limits
30
irb > Setting.user_limits = 45
irb > Setting.user_limits
45

irb > Setting.captcha_enable
1
irb > Setting.captcha_enable?
true
irb > Setting.captcha_enable = "0"
irb > Setting.captcha_enable
false
irb > Setting.captcha_enable = "1"
irb > Setting.captcha_enable
true
irb > Setting.captcha_enable = "false"
irb > Setting.captcha_enable
false
irb > Setting.captcha_enable = "true"
irb > Setting.captcha_enable
true
irb > Setting.captcha_enable?
true

irb > Setting.admin_emails
["[email protected]"]
irb > Setting.admin_emails = %w[[email protected] [email protected]]
irb > Setting.admin_emails
["[email protected]", "[email protected]"]
irb > Setting.admin_emails = "[email protected],[email protected]\n[email protected]"
irb > Setting.admin_emails
["[email protected]", "[email protected]", "[email protected]"]

irb > Setting.notification_options
{
  send_all: true,
  logging: true,
  sender_email: "[email protected]"
}
irb > Setting.notification_options = {
  sender_email: "[email protected]"
}
irb > Setting.notification_options
{
  sender_email: "[email protected]"
}

Get defined fields

version 2.3+

# Get all keys
Setting.keys
=> ["app_name", "host", "default_locale", "readonly_item"]

# Get editable keys
Setting.editable_keys
=> ["app_name", "default_locale"]

# Get readonly keys
Setting.readonly_keys
=> ["host", "readonly_item"]

# Get field
Setting.get_field("host")
=> { scope: :application, key: "host", type: :string, default: "http://example.com", readonly: true }
Setting.get_field("app_name")
=> { scope: :application, key: "app_name", type: :string, default: "Rails Settings", readonly: false }
Setting.get_field(:user_limits)
=> { scope: :limits, key: "user_limits", type: :integer, default: 20, readonly: false }
# Get field options
Setting.get_field("default_locale")[:options]
=> { option_values: %w[en zh-CN jp], help_text: "Bla bla ..." }

Custom type for setting

Since: 2.9.0

You can write your custom field type by under RailsSettings::Fields module.

For example

module RailsSettings
  module Fields
    class YesNo < ::RailsSettings::Fields::Base
      def serialize(value)
        case value
        when true then "YES"
        when false then "NO"
        else raise StandardError, 'invalid value'
        end
      end

      def deserialize(value)
        case value
        when "YES" then true
        when "NO" then false
        else nil
        end
      end
    end
  end
end

Now you can use yes_no type in you setting:

class Setting
  field :custom_item, type: :yes_no, default: 'YES'
end
irb> Setting.custom_item = 'YES'
irb> Setting.custom_item
true
irb> Setting.custom_item = 'NO'
irb> Setting.custom_item
false

Get All defined fields

version 2.7.0+

You can use defined_fields method to get all defined fields in Setting.

# Get editable fields and group by scope
editable_fields = Setting.defined_fields
  .select { |field| !field[:readonly] }
  .group_by { |field| field[:scope] }

Validations

You can use validates options to special the Rails Validation for fields.

class Setting < RailsSettings::Base
  # cache_prefix { "v1" }
  field :app_name, default: "Rails Settings", validates: { presence: true, length: { in: 2..20 } }
  field :default_locale, default: "zh-CN", validates: { presence: true, inclusion: { in: %w[zh-CN en jp], message: "is not included in [zh-CN, en, jp]" } }
end

Now validate will work on record save:

irb> Setting.app_name = ""
ActiveRecord::RecordInvalid: (Validation failed: App name can't be blank)
irb> Setting.app_name = "Rails Settings"
"Rails Settings"
irb> Setting.default_locale = "zh-TW"
ActiveRecord::RecordInvalid: (Validation failed: Default locale is not included in [zh-CN, en, jp])
irb> Setting.default_locale = "en"
"en"

Validate by save / valid? method:

setting = Setting.find_or_initialize_by(var: :app_name)
setting.value = ""
setting.valid?
# => false
setting.errors.full_messages
# => ["App name can't be blank", "App name too short (minimum is 2 characters)"]

setting = Setting.find_or_initialize_by(var: :default_locale)
setting.value = "zh-TW"
setting.save
# => false
setting.errors.full_messages
# => ["Default locale is not included in [zh-CN, en, jp]"]
setting.value = "en"
setting.valid?
# => true

Use Setting in Rails initializing:

In version 2.3+ you can use Setting before Rails is initialized.

For example config/initializers/devise.rb

Devise.setup do |config|
  if Setting.omniauth_google_client_id.present?
    config.omniauth :google_oauth2, Setting.omniauth_google_client_id, Setting.omniauth_google_client_secret
  end
end
class Setting < RailsSettings::Base
  field :omniauth_google_client_id, default: ENV["OMNIAUTH_GOOGLE_CLIENT_ID"]
  field :omniauth_google_client_secret, default: ENV["OMNIAUTH_GOOGLE_CLIENT_SECRET"]
end

Readonly field

You may also want use Setting before Rails initialize:

config/environments/*.rb

If you want do that do that, the setting field must has readonly: true.

For example:

class Setting < RailsSettings::Base
  field :mailer_provider, default: (ENV["mailer_provider"] || "smtp"), readonly: true
  field :mailer_options, type: :hash, readonly: true, default: {
    address: ENV["mailer_options.address"],
    port: ENV["mailer_options.port"],
    domain: ENV["mailer_options.domain"],
    user_name: ENV["mailer_options.user_name"],
    password: ENV["mailer_options.password"],
    authentication: ENV["mailer_options.authentication"] || "login",
    enable_starttls_auto: ENV["mailer_options.enable_starttls_auto"]
  }
end

config/environments/production.rb

# You must require_relative directly in Rails 6.1+ in config/environments/production.rb
require_relative "../../app/models/setting"

Rails.application.configure do
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = Setting.mailer_options.deep_symbolize_keys
end

TIP: You also can follow this file to rewrite ActionMailer's mail method for configuration Mail options from Setting after Rails booted.

https://github.com/ruby-china/homeland/blob/main/app/mailers/application_mailer.rb#L19

Caching flow:

Setting.host -> Check Cache -> Exist - Get value of key for cache -> Return
                   |
                Fetch all key and values from DB -> Write Cache -> Get value of key for cache -> return
                   |
                Return default value or nil

In each Setting keys call, we will load the cache/db and save in ActiveSupport::CurrentAttributes to avoid hit cache/db.

Each key update will expire the cache, so do not add some frequent update key.

Change cache key

Some times you may need to force update cache, now you can use cache_prefix

class Setting < RailsSettings::Base
  cache_prefix { "you-prefix" }
  ...
end

In testing, you need add Setting.clear_cache for each Test case:

class ActiveSupport::TestCase
  teardown do
    Setting.clear_cache
  end
end

How to manage Settings in the admin interface?

If you want to create an admin interface to editing the Settings, you can try methods in following:

config/routes.rb

namespace :admin do
  resource :settings
end

app/controllers/admin/settings_controller.rb

module Admin
  class SettingsController < ApplicationController
    def create
      @errors = ActiveModel::Errors.new
      setting_params.keys.each do |key|
        next if setting_params[key].nil?

        setting = Setting.new(var: key)
        setting.value = setting_params[key].strip
        unless setting.valid?
          @errors.merge!(setting.errors)
        end
      end

      if @errors.any?
        render :new
      end

      setting_params.keys.each do |key|
        Setting.send("#{key}=", setting_params[key].strip) unless setting_params[key].nil?
      end

      redirect_to admin_settings_path, notice: "Setting was successfully updated."
    end

    private
      def setting_params
        params.require(:setting).permit(:host, :user_limits, :admin_emails,
          :captcha_enable, :notification_options)
      end
  end
end

app/views/admin/settings/show.html.erb

<%= form_for(Setting.new, url: admin_settings_path) do |f| %>
  <% if @errors.any? %>
    <div class="alert alert-block alert-danger">
      <ul>
        <% @errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div class="form-group">
    <label class="control-label">Host</label>
    <%= f.text_field :host, value: Setting.host, class: "form-control", placeholder: "http://localhost"  %>
  </div>

  <div class="form-group form-checkbox">
    <label>
      <%= f.check_box :captcha_enable, checked: Setting.captcha_enable? %>
      Enable/Disable Captcha
    </label>
  </div>

  <div class="form-group">
    <label class="control-label">Admin Emails</label>
    <%= f.text_area :admin_emails, value: Setting.admin_emails.join("\n"), class: "form-control" %>
  </div>

  <div class="form-group">
    <label class="control-label">Notification options</label>
    <%= f.text_area :notification_options, value: YAML.dump(Setting.notification_options), class: "form-control", style: "height: 180px;"  %>
    <div class="form-text">
      Use YAML format to config the SMTP_html
    </div>
  </div>

  <div>
    <%= f.submit 'Update Settings' %>
  </div>
<% end %>

Scoped Settings

🚨 BREAK CHANGES WARNING: rails-settings-cached 2.x has redesigned the API, the new version will compatible with the stored setting values by an older version. When you want to upgrade 2.x, you must read the README again, and follow guides to change your Setting model. 0.x stable branch: https://github.com/huacnlee/rails-settings-cached/tree/0.x

For new project / new user of rails-settings-cached. The ActiveRecord::AttributeMethods::Serialization is best choice.

This is reason of why rails-settings-cached 2.x removed Scoped Settings feature.

For example:

We wants a preferences setting for user.

class User < ActiveRecord::Base
  serialize :preferences
end

@user = User.new
@user.preferences[:receive_emails] = true
@user.preferences[:public_email] = true
@user.save

Use cases:

And more than 1K repositories used.

More Repositories

1

flora-kit

💐 基于 shadowsocks-go 做的完善实现,自动网络分流,完全兼容 Surge 的配置文件。
Go
898
star
2

autocorrect

A linter and formatter to help you to improve copywriting, correct spaces, words, and punctuations between CJK (Chinese, Japanese, Korean).
Rust
887
star
3

redis-search

Deprecated! High performance real-time prefix search, indexes store in Redis for Rails application
Ruby
712
star
4

rucaptcha

Captcha Gem for Rails, which generates captcha image by Rust.
Ruby
686
star
5

quora

Quora.com like project with Ruby on Rails (不再维护)
JavaScript
684
star
6

init.d

⚙️ Batch scripts for Rails production environment install on Ubuntu Server.
678
star
7

bluedoc

An open-source document management tool for enterprise self host.
Ruby
626
star
8

social-share-button

Helper for add social share feature in your Rails app. Twitter, Facebook, Weibo, Douban ...
CoffeeScript
581
star
9

PokemonGoMove

Pokemon GO iOS GPS Emulator - NO Jailbreak needed, lets you play the game on your Mac :)
Python
412
star
10

imax.im

🎬 Source code of IMAX.im
Ruby
366
star
11

mediom

Forum web application, an example for from Rails to Go (Revel)
Go
355
star
12

jquery.qeditor

This is a simple WYSIWYG editor with jQuery.
CoffeeScript
259
star
13

carrierwave-aliyun

阿里云 OSS Ruby 上传组件,基于 Carrierwave
Ruby
195
star
14

auto-correct

Automatically add whitespace between CJK (Chinese, Japanese, Korean) and half-width characters (alphabetical letters, numerical digits and symbols).
Ruby
142
star
15

activestorage-aliyun

Wraps the Aliyun OSS as an Active Storage service.
Ruby
131
star
16

sails

Create a Thrift Server use like Rails
Ruby
71
star
17

turbolinks-prefetch

Turbolinks extends for prefetch links to speeds up your website.
JavaScript
70
star
18

personlab

我博客的源代码,这个代码较老,不推荐拿来学习
Ruby
69
star
19

vimmate

Custom vim like Textmate for Ruby on Rails development
Vim Script
69
star
20

gpui-component

A UI components write in GPUI.
Rust
67
star
21

mongoid_auto_increment_id

Override id field to MySQL like auto increment for Mongoid.
Ruby
65
star
22

hello-go

入门 Go 编写应用
Go
57
star
23

pasite

Share your sources code on the web, see the http://pasite.org
Ruby
55
star
24

zed-theme-macos-classic

A macOS native style theme for Zed, let it same like native app in macOS.
JavaScript
49
star
25

sql-builder

A simple SQL builder for generate SQL for non-ActiveRecord supports databases
Ruby
36
star
26

rails-activestorage-example

Rails use Active Storage the right way
Ruby
30
star
27

redmine-theme-innerboard

Innerboard theme for Redmine
26
star
28

cocoaout

Auto build and release tool for Cocoa projects.
Ruby
25
star
29

enumize

Extend ActiveRecord::Enum for add more helpful methods.
Ruby
24
star
30

jquery.lazyimg

Image lazy load plugin for jQuery, fork from jquery.unveil to improve performance with huge DOMs.
CoffeeScript
23
star
31

gitlab-mail-receiver

The way of allow your GitLab support Email receive and parse the email content, and find Issue/MergeRequest to create reply.
Ruby
23
star
32

redis-search-example

An example for use redis-search gem
JavaScript
23
star
33

ip-location

通过淘宝 IP 库查询 IP 所在地域位置 http://ip.taobao.com
Ruby
18
star
34

mongoid_taggable_on

Taggable on custom fields for Mongoid
Ruby
16
star
35

vscode-macos-classic.theme

macOS Classic theme for Visual Studio Code
Makefile
15
star
36

backup-aliyun

Aliyun OSS storage with Backup
Ruby
12
star
37

jdialog

a jQuery popup window plugin
JavaScript
12
star
38

html-pipeline.cr

HTML processing filters and utilities for Crystal.
Crystal
11
star
39

vue-rails-example

Use Vue.js in Rails 6 example
Ruby
9
star
40

actiontext-lite

Lite version of ActionText
Ruby
7
star
41

autocorrect-action

GitHub action for use AutoCorrect as lint
Shell
6
star
42

zed-extension-action

GitHub Action for automatically bump Zed Extensions version after a release.
JavaScript
6
star
43

docker-rails

Deploy Rails via Docker example
Ruby
5
star
44

booklib

php + Codeigniter Library management tool
PHP
4
star
45

vscode-blackboard-plus.theme

Visual Studio Code - Blackboard Plus Theme
4
star
46

zed-csv

CSV support for Zed
Rust
4
star
47

remarkdown

This is extends of Markdown lib from Crystal Stdlib for Support Markdown GFM.
Crystal
4
star
48

huacnlee.github.io

Ruby
3
star
49

vscode-autocorrect

AutoCorrect for VS Code
TypeScript
3
star
50

sidekiq-activerecord-shard

Sidekiq middleware to supports ActiveRecord 7 shard
Ruby
3
star
51

autocorrect-idea-plugin

AutoCorrect Plugin for IntelliJ IDEA
Kotlin
2
star
52

auto-booking-park

Shell
2
star
53

jselectdate

jQuery date choice control with dropdown list
JavaScript
2
star
54

capistrano-upload-configs

Capistrano plugin for Upload local config files to remote, and create soft link.
Ruby
2
star
55

zed-autocorrect

AutoCorrect for Zed
Rust
2
star
56

micro-web-simple

Go
1
star
57

jimagelink

模拟yupoo的缩略图上的小图标效果的jQuery插件
JavaScript
1
star
58

jcaches

client side cache with Javascript
JavaScript
1
star
59

yjs-server

JavaScript
1
star
60

Light-Classic.tmTheme

Improve of TextMate Light Theme from Mac Classic
1
star