rails settings cached

Delphi源码 2025-08-13

rails settings cached

在Rails应用程序中存储全球设置的最佳解决方案。

该宝石将使管理全局密钥的表,值对。可以将其视为存储在您的数据库中的全局哈希,它使用了简单的ActivereCord进行操作的方法。跟踪您不想将其硬代码纳入Rails应用程序的任何全局设置。

安装

编辑您的Gemfile:

$ bundle add rails-settings-cached

生成您的设置:

$ rails g settings:install

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

您将获得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://*ex*am*ple.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 ]

    # 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 : "foo@bar.com"
  }

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

您必须使用field方法来对设置键进行声明,否则您不能使用它。

scope方法允许您对admin UI的键进行分组。

现在只将该迁移放在数据库中:

$ rails db:migrate

用法

语法很容易。首先,让我们创建一些设置以跟踪:

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 ["admin@rubyonrails.org"] irb > Setting.admin_emails = %w[foo@bar.com bar@dar.com] irb > Setting.admin_emails ["foo@bar.com", "bar@dar.com"] irb > Setting.admin_emails = "huacnlee@gmail.com,admin@admin.com\nadmin@rubyonrails.org" irb > Setting.admin_emails ["huacnlee@gmail.com", "admin@admin.com", "admin@rubyonrails.org"] irb > Setting.notification_options { send_all: true, logging: true, sender_email: "foo@bar.com" } irb > Setting.notification_options = { sender_email: "notice@rubyonrails.org" } irb > Setting.notification_options { sender_email: "notice@rubyonrails.org" }">
 irb > Setting . host
"http://*ex*am*ple.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
[ "admin@rubyonrails.org" ]
irb > Setting . admin_emails = %w[ foo@bar.com bar@dar.com ]
irb > Setting . admin_emails
[ "foo@bar.com" , "bar@dar.com" ]
irb > Setting . admin_emails = "huacnlee@gmail.com,admin@admin.com \n admin@rubyonrails.org"
irb > Setting . admin_emails
[ "huacnlee@gmail.com" , "admin@admin.com" , "admin@rubyonrails.org" ]

irb > Setting . notification_options
{
  send_all : true ,
  logging : true ,
  sender_email : "foo@bar.com"
}
irb > Setting . notification_options = {
  sender_email : "notice@rubyonrails.org"
}
irb > Setting . notification_options
{
  sender_email : "notice@rubyonrails.org"
}

获取定义的字段

版本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://*ex*am*ple.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 ..." }

定制类型用于设置

自:2.9.0

您可以通过RailsSettings::Fields模块编写自定义字段类型。

例如

 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

现在,您可以在设置中使用yes_no类型:

 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 

获取所有定义的字段

版本2.7.0+

您可以使用defined_fields方法在设置中获取所有定义的字段。

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

验证

您可以使用validates选项来特殊轨道验证字段。

 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

现在,验证将在记录上工作:

 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"

通过save / valid?方法:

 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 

在轨道中使用设置初始化:

version 2.3+您可以在初始化导轨之前使用设置。

例如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字段

您可能还需要在Rails初始化之前使用设置:

 config/environments/*.rb

如果您想这样做,则设置字段必须具有readonly: true

例如:

 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

配置/环境/生产.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

提示:您还可以按照此文件来重写ActionMailer的mail方法,以便从启动导轨后设置配置邮件选项。

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

缓存流:

 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

在每个设置键调用中,我们将加载缓存/db并保存在ActiveSupport :: CurrentAttributes中,以避免击中缓存/DB。

每个密钥更新将过期缓存,因此请勿添加一些频繁的更新密钥。

更改缓存键

有时您可能需要强制更新缓存,现在您可以使用cache_prefix

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

在测试中,您需要添加Setting.clear_cache 。每种测试用例Clear_cache:

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

如何管理管理接口中的设置?

如果要创建管理界面以编辑设置,则可以尝试以下方法:

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://*loca*lho*st"  %>
  </ 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 %> 

特殊的缓存存储

您可以使用cache_store更改缓存存储,默认为Rails.cache

添加config/initializers/rails_settings.rb

 RailsSettings . configure do
  self . cache_storage = ActiveSupport :: Cache :: RedisCacheStore . new ( url : "redis://localhost:6379" )
end 

范围设置

休息更改警告:Rails-Settings-Sached 2.X已重新设计了API,新版本将与较旧版本的存储设置值兼容。当您想升级2.x时,必须再次阅读读书文件,然后遵循指南以更改设置模型。 0.x稳定分支:https://github.com/huacnlee/rails-settings-cached/tree/0.x

  • 向后兼容以支持0.x范围的设置

适用于新项目 /轨道静止的新用户。 ActivereCord :: AttributeHods ::序列化是最佳选择。

这就是为什么轨道插座的2.x删除范围示波器设置功能的原因

下载源码

通过命令行克隆项目:

git clone https://github.com/huacnlee/rails-settings-cached.git