All Projects → huacnlee → rails-settings-cached

huacnlee / rails-settings-cached

Licence: MIT license
Global settings for your Rails application.

Programming Languages

ruby
36898 projects - #4 most used programming language
CSS
56736 projects
javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to rails-settings-cached

eslint-define-config
Provide a defineConfig function for .eslintrc.js files
Stars: ✭ 61 (-93.51%)
Mutual labels:  config, configuration
dotfiles
My personal app/env configs and dotfiles.
Stars: ✭ 27 (-97.13%)
Mutual labels:  config, configuration
config
Config component, strictly typed
Stars: ✭ 14 (-98.51%)
Mutual labels:  config, configuration
salak.rs
A multi layered configuration loader and zero-boilerplate configuration parser.
Stars: ✭ 27 (-97.13%)
Mutual labels:  config, configuration
spdlog setup
spdlog setup initialization via file configuration for convenience.
Stars: ✭ 68 (-92.77%)
Mutual labels:  config, configuration
network tech
Cisco config syntax and snippets for Sublime Text
Stars: ✭ 82 (-91.28%)
Mutual labels:  config, configuration
onion
Layer based configuration for golang
Stars: ✭ 104 (-88.94%)
Mutual labels:  config, configuration
croconf
A flexible and composable configuration library for Go that doesn't suck
Stars: ✭ 14 (-98.51%)
Mutual labels:  config, configuration
parse it
A python library for parsing multiple types of config files, envvars & command line arguments that takes the headache out of setting app configurations.
Stars: ✭ 86 (-90.85%)
Mutual labels:  config, configuration
tomlj
A Java parser for Tom's Obvious, Minimal Language (TOML).
Stars: ✭ 72 (-92.34%)
Mutual labels:  config, configuration
configuro
An opinionated configuration loading framework for Containerized and Cloud-Native applications.
Stars: ✭ 81 (-91.38%)
Mutual labels:  config, configuration
neovim-config
Modern NeoVim config for IDE-like development
Stars: ✭ 89 (-90.53%)
Mutual labels:  config, configuration
climatecontrol
Python library for loading settings and config data from files and environment variables
Stars: ✭ 20 (-97.87%)
Mutual labels:  config, configuration
ha-config-ataraxis
My Home Assistant Configs. If you like what you see, please ⭐️my repo. It would encourage me a lot 🤘
Stars: ✭ 146 (-84.47%)
Mutual labels:  config, configuration
cfg-rs
A Configuration Library for Rust Applications
Stars: ✭ 18 (-98.09%)
Mutual labels:  config, configuration
nginx-conf
Nginx configuration
Stars: ✭ 18 (-98.09%)
Mutual labels:  config, configuration
superconfig
Access environment variables. Also includes presence validation, type coercion and default values.
Stars: ✭ 33 (-96.49%)
Mutual labels:  config, configuration
nvim
❤️ A neovim config repo.
Stars: ✭ 33 (-96.49%)
Mutual labels:  config, configuration
Machfiles
The dotfiles you see in all my videos
Stars: ✭ 347 (-63.09%)
Mutual labels:  config, configuration
profig
Powerful configuration management for Scala (JSON, properties, command-line arguments, and environment variables)
Stars: ✭ 25 (-97.34%)
Mutual labels:  config, configuration

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 ..." }

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.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].