rails-settings-cached 2.3.4 → 2.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +122 -33
- data/lib/rails-settings-cached.rb +1 -0
- data/lib/rails-settings/base.rb +20 -14
- data/lib/rails-settings/request_cache.rb +30 -0
- data/lib/rails-settings/version.rb +1 -1
- metadata +16 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 29703d30ef00244c99b49047ce3a05edd212ee71a23433cb581d5bf69c6a02c6
|
4
|
+
data.tar.gz: 7f1d2dffdf1c552faca1b7afdbaaeaa52039897afb5364b8aef37de3d93389c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e1b41ac975dd31fadf90c5d9c059542aaeb09a0bfbf3f137266a32ae58c30557afedbe33c775af9a2683a8f8b56b85ee0f94b4a063a27e5c79176b5c1714d0d
|
7
|
+
data.tar.gz: a5ee74e8dd391aab429db68904b5d78a79581fd41de6304aece887fcdf9be8b023f4fff03a2fedbfdbc81cff964be5b695a90c5e4c3f908d49bed01c0fb70f83
|
data/README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# Rails Settings Cached
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
that uses simple ActiveRecord like methods for manipulation. Keep track of any global
|
6
|
-
|
7
|
-
of object. Strings, numbers, arrays, or any object.
|
3
|
+
The best solution for store global settings in Rails applications.
|
4
|
+
|
5
|
+
This gem will 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 app.
|
6
|
+
|
7
|
+
You can store any kind of object. Strings, numbers, arrays, booleans, or any object.
|
8
8
|
|
9
9
|
[![Gem Version](https://badge.fury.io/rb/rails-settings-cached.svg)](https://rubygems.org/gems/rails-settings-cached) [![CI Status](https://travis-ci.org/huacnlee/rails-settings-cached.svg)](http://travis-ci.org/huacnlee/rails-settings-cached) [![codecov.io](https://codecov.io/github/huacnlee/rails-settings-cached/coverage.svg?branch=master)](https://codecov.io/github/huacnlee/rails-settings-cached?branch=master)
|
10
10
|
|
@@ -30,9 +30,9 @@ You will get `app/models/setting.rb`
|
|
30
30
|
```rb
|
31
31
|
class Setting < RailsSettings::Base
|
32
32
|
# cache_prefix { "v1" }
|
33
|
-
field :app_name, default: "Rails Settings"
|
33
|
+
field :app_name, default: "Rails Settings", validates: { presence: true, length: { in: 2..20 } }
|
34
34
|
field :host, default: "http://example.com", readonly: true
|
35
|
-
field :default_locale, default: "zh-CN"
|
35
|
+
field :default_locale, default: "zh-CN", validates: { presence: true, inclusion: { in: %w[zh-CN en jp] } }
|
36
36
|
field :readonly_item, type: :integer, default: 100, readonly: true
|
37
37
|
field :user_limits, type: :integer, default: 20
|
38
38
|
field :exchange_rate, type: :float, default: 0.123
|
@@ -49,11 +49,11 @@ class Setting < RailsSettings::Base
|
|
49
49
|
}
|
50
50
|
|
51
51
|
# lambda default value
|
52
|
-
field :welcome_message, type: :string, default: -> { "welcome to #{self.app_name}" }
|
52
|
+
field :welcome_message, type: :string, default: -> { "welcome to #{self.app_name}" }, validates: { length: { maximum: 255 } }
|
53
53
|
end
|
54
54
|
```
|
55
55
|
|
56
|
-
You must use `field` method to statement the setting keys, otherwise you can't use it.
|
56
|
+
You must use the `field` method to statement the setting keys, otherwise you can't use it.
|
57
57
|
|
58
58
|
Now just put that migration in the database with:
|
59
59
|
|
@@ -126,7 +126,6 @@ irb > Setting.notification_options
|
|
126
126
|
}
|
127
127
|
```
|
128
128
|
|
129
|
-
|
130
129
|
### Get defined fields
|
131
130
|
|
132
131
|
> version 2.3+
|
@@ -151,9 +150,58 @@ Setting.get_field("app_name")
|
|
151
150
|
=> { key: "app_name", type: :string, default: "Rails Settings", readonly: false }
|
152
151
|
```
|
153
152
|
|
154
|
-
##
|
153
|
+
## Validations
|
154
|
+
|
155
|
+
You can use `validates` options to special the [Rails Validation](https://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validates) for fields.
|
156
|
+
|
157
|
+
```rb
|
158
|
+
class Setting < RailsSettings::Base
|
159
|
+
# cache_prefix { "v1" }
|
160
|
+
field :app_name, default: "Rails Settings", validates: { presence: true, length: { in: 2..20 } }
|
161
|
+
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]" } }
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
Now validate will work on record save:
|
155
166
|
|
156
|
-
|
167
|
+
```rb
|
168
|
+
irb> Setting.app_name = ""
|
169
|
+
ActiveRecord::RecordInvalid: (Validation failed: App name can't be blank)
|
170
|
+
irb> Setting.app_name = "Rails Settings"
|
171
|
+
"Rails Settings"
|
172
|
+
irb> Setting.default_locale = "zh-TW"
|
173
|
+
ActiveRecord::RecordInvalid: (Validation failed: Default locale is not included in [zh-CN, en, jp])
|
174
|
+
irb> Setting.default_locale = "en"
|
175
|
+
"en"
|
176
|
+
```
|
177
|
+
|
178
|
+
Validate by `save` / `valid?` method:
|
179
|
+
|
180
|
+
```rb
|
181
|
+
|
182
|
+
setting = Setting.find_or_initialize_by(var: :app_name)
|
183
|
+
setting.value = ""
|
184
|
+
setting.valid?
|
185
|
+
# => false
|
186
|
+
setting.errors.full_messages
|
187
|
+
# => ["App name can't be blank", "App name too short (minimum is 2 characters)"]
|
188
|
+
|
189
|
+
setting = Setting.find_or_initialize_by(var: :default_locale)
|
190
|
+
setting.value = "zh-TW"
|
191
|
+
setting.save
|
192
|
+
# => false
|
193
|
+
setting.errors.full_messages
|
194
|
+
# => ["Default locale is not included in [zh-CN, en, jp]"]
|
195
|
+
setting.value = "en"
|
196
|
+
setting.valid?
|
197
|
+
# => true
|
198
|
+
```
|
199
|
+
|
200
|
+
## Use Setting in Rails initializing:
|
201
|
+
|
202
|
+
In `version 2.3+` you can use Setting before Rails is initialized.
|
203
|
+
|
204
|
+
For example `config/initializers/devise.rb`
|
157
205
|
|
158
206
|
```rb
|
159
207
|
Devise.setup do |config|
|
@@ -163,37 +211,53 @@ Devise.setup do |config|
|
|
163
211
|
end
|
164
212
|
```
|
165
213
|
|
166
|
-
In this case, you must define the `readonly` field:
|
167
|
-
|
168
214
|
```rb
|
169
215
|
class Setting < RailsSettings::Base
|
170
|
-
|
171
|
-
field :
|
172
|
-
field :omniauth_google_client_secret, default: ENV["OMNIAUTH_GOOGLE_CLIENT_SECRET"], readonly: true
|
216
|
+
field :omniauth_google_client_id, default: ENV["OMNIAUTH_GOOGLE_CLIENT_ID"]
|
217
|
+
field :omniauth_google_client_secret, default: ENV["OMNIAUTH_GOOGLE_CLIENT_SECRET"]
|
173
218
|
end
|
174
219
|
```
|
175
220
|
|
176
|
-
##
|
221
|
+
## Readonly field
|
177
222
|
|
178
|
-
You
|
223
|
+
You may also want use Setting before Rails initialize:
|
179
224
|
|
180
225
|
```
|
181
|
-
config/application.rb
|
182
226
|
config/environments/*.rb
|
183
227
|
```
|
184
228
|
|
185
|
-
If you
|
229
|
+
If you want do that do that, the setting field must has `readonly: true`.
|
186
230
|
|
187
231
|
For example:
|
188
232
|
|
189
233
|
```rb
|
190
|
-
|
191
|
-
|
192
|
-
|
234
|
+
class Setting < RailsSettings::Base
|
235
|
+
field :mailer_provider, default: (ENV["mailer_provider"] || "smtp"), readonly: true
|
236
|
+
field :mailer_options, type: :hash, readonly: true, default: {
|
237
|
+
address: ENV["mailer_options.address"],
|
238
|
+
port: ENV["mailer_options.port"],
|
239
|
+
domain: ENV["mailer_options.domain"],
|
240
|
+
user_name: ENV["mailer_options.user_name"],
|
241
|
+
password: ENV["mailer_options.password"],
|
242
|
+
authentication: ENV["mailer_options.authentication"] || "login",
|
243
|
+
enable_starttls_auto: ENV["mailer_options.enable_starttls_auto"]
|
244
|
+
}
|
245
|
+
end
|
246
|
+
```
|
247
|
+
|
248
|
+
config/environments/production.rb
|
249
|
+
|
250
|
+
```rb
|
251
|
+
# You must require_relative directly in Rails 6.1+ in config/environments/production.rb
|
252
|
+
require_relative "../../app/models/setting"
|
253
|
+
|
254
|
+
Rails.application.configure do
|
255
|
+
config.action_mailer.delivery_method = :smtp
|
256
|
+
config.action_mailer.smtp_settings = Setting.mailer_options.deep_symbolize_keys
|
193
257
|
end
|
194
258
|
```
|
195
259
|
|
196
|
-
|
260
|
+
## Caching flow:
|
197
261
|
|
198
262
|
```
|
199
263
|
Setting.host -> Check Cache -> Exist - Get value of key for cache -> Return
|
@@ -203,7 +267,7 @@ Setting.host -> Check Cache -> Exist - Get value of key for cache -> Return
|
|
203
267
|
Return default value or nil
|
204
268
|
```
|
205
269
|
|
206
|
-
In each Setting keys call, we will load the cache/db and save in [
|
270
|
+
In each Setting keys call, we will load the cache/db and save in [ActiveSupport::CurrentAttributes](https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html) to avoid hit cache/db.
|
207
271
|
|
208
272
|
Each key update will expire the cache, so do not add some frequent update key.
|
209
273
|
|
@@ -228,7 +292,7 @@ class ActiveSupport::TestCase
|
|
228
292
|
end
|
229
293
|
```
|
230
294
|
|
231
|
-
|
295
|
+
---
|
232
296
|
|
233
297
|
## How to manage Settings in the admin interface?
|
234
298
|
|
@@ -242,19 +306,32 @@ namespace :admin do
|
|
242
306
|
end
|
243
307
|
```
|
244
308
|
|
245
|
-
|
246
309
|
app/controllers/admin/settings_controller.rb
|
247
310
|
|
248
311
|
```rb
|
249
312
|
module Admin
|
250
313
|
class SettingsController < ApplicationController
|
251
|
-
before_action :get_setting, only: [:edit, :update]
|
252
|
-
|
253
314
|
def create
|
315
|
+
@errors = ActiveModel::Errors.new
|
316
|
+
setting_params.keys.each do |key|
|
317
|
+
next if setting_params[key].nil?
|
318
|
+
|
319
|
+
setting = Setting.new(var: key)
|
320
|
+
setting.value = setting_params[key].strip
|
321
|
+
unless setting.valid?
|
322
|
+
@errors.merge!(setting.errors)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
if @errors.any?
|
327
|
+
render :new
|
328
|
+
end
|
329
|
+
|
254
330
|
setting_params.keys.each do |key|
|
255
331
|
Setting.send("#{key}=", setting_params[key].strip) unless setting_params[key].nil?
|
256
332
|
end
|
257
|
-
|
333
|
+
|
334
|
+
redirect_to admin_settings_path, notice: "Setting was successfully updated."
|
258
335
|
end
|
259
336
|
|
260
337
|
private
|
@@ -270,6 +347,16 @@ app/views/admin/settings/show.html.erb
|
|
270
347
|
|
271
348
|
```erb
|
272
349
|
<%= form_for(Setting.new, url: admin_settings_path) do |f| %>
|
350
|
+
<% if @errors.any? %>
|
351
|
+
<div class="alert alert-block alert-danger">
|
352
|
+
<ul>
|
353
|
+
<% @errors.full_messages.each do |msg| %>
|
354
|
+
<li><%= msg %></li>
|
355
|
+
<% end %>
|
356
|
+
</ul>
|
357
|
+
</div>
|
358
|
+
<% end %>
|
359
|
+
|
273
360
|
<div class="form-group">
|
274
361
|
<label class="control-label">Host</label>
|
275
362
|
<%= f.text_field :host, value: Setting.host, class: "form-control", placeholder: "http://localhost" %>
|
@@ -335,9 +422,11 @@ end
|
|
335
422
|
- [forem/forem](https://github.com/forem/forem) - 2.x
|
336
423
|
- [siwapp/siwapp](https://github.com/siwapp/siwapp) - 2.x
|
337
424
|
- [aidewoode/black_candy](https://github.com/aidewoode/black_candy) - 2.x
|
338
|
-
- [
|
425
|
+
- [huacnlee/bluedoc](https://github.com/huacnlee/bluedoc) - 2.x
|
426
|
+
- [getzealot/zealot](https://github.com/getzealot/zealot) - 2.x
|
427
|
+
- [kaishuu0123/rebacklogs](https://github.com/kaishuu0123/rebacklogs) - 2.x
|
339
428
|
- [tootsuite/mastodon](https://github.com/tootsuite/mastodon) - 0.6.x
|
340
429
|
- [helpyio/helpy](https://github.com/helpyio/helpy) - 0.5.x
|
341
|
-
|
430
|
+
- [daqing/rabel](https://github.com/daqing/rabel) - 0.4.x
|
342
431
|
|
343
432
|
And more than [1K repositories](https://github.com/huacnlee/rails-settings-cached/network/dependents) used.
|
data/lib/rails-settings/base.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "request_store"
|
4
|
-
|
5
3
|
module RailsSettings
|
6
4
|
class Base < ActiveRecord::Base
|
7
5
|
class SettingNotFound < RuntimeError; end
|
@@ -25,12 +23,12 @@ module RailsSettings
|
|
25
23
|
|
26
24
|
class << self
|
27
25
|
def clear_cache
|
28
|
-
|
26
|
+
RequestCache.reset
|
29
27
|
Rails.cache.delete(cache_key)
|
30
28
|
end
|
31
29
|
|
32
30
|
def field(key, **opts)
|
33
|
-
_define_field(key, default: opts[:default], type: opts[:type], readonly: opts[:readonly], separator: opts[:separator])
|
31
|
+
_define_field(key, default: opts[:default], type: opts[:type], readonly: opts[:readonly], separator: opts[:separator], validates: opts[:validates])
|
34
32
|
end
|
35
33
|
|
36
34
|
def get_field(key)
|
@@ -61,7 +59,7 @@ module RailsSettings
|
|
61
59
|
|
62
60
|
private
|
63
61
|
|
64
|
-
def _define_field(key, default: nil, type: :string, readonly: false, separator: nil)
|
62
|
+
def _define_field(key, default: nil, type: :string, readonly: false, separator: nil, validates: nil)
|
65
63
|
@defined_fields ||= []
|
66
64
|
@defined_fields << {
|
67
65
|
key: key.to_s,
|
@@ -101,6 +99,15 @@ module RailsSettings
|
|
101
99
|
|
102
100
|
value
|
103
101
|
end
|
102
|
+
|
103
|
+
if validates
|
104
|
+
validates[:if] = Proc.new { |item| item.var.to_s == key.to_s }
|
105
|
+
send(:validates, key, **validates)
|
106
|
+
|
107
|
+
define_method(:read_attribute_for_validation) do |_key|
|
108
|
+
self.value
|
109
|
+
end
|
110
|
+
end
|
104
111
|
end
|
105
112
|
|
106
113
|
if type == :boolean
|
@@ -142,20 +149,19 @@ module RailsSettings
|
|
142
149
|
end
|
143
150
|
|
144
151
|
def _value_of(var_name)
|
145
|
-
unless
|
152
|
+
unless _table_exists?
|
146
153
|
# Fallback to default value if table was not ready (before migrate)
|
147
|
-
puts "WARNING: table: \"#{table_name}\" does not exist, `#{name}.#{var_name}` fallback to returns the default value."
|
154
|
+
puts "WARNING: table: \"#{table_name}\" does not exist or not database connection, `#{name}.#{var_name}` fallback to returns the default value."
|
148
155
|
return nil
|
149
156
|
end
|
150
157
|
|
151
158
|
_all_settings[var_name.to_s]
|
152
|
-
|
153
|
-
if e.message.include?("connect")
|
154
|
-
puts "WARNING: `#{name}.#{var_name}` called but no connection, fallback to returns the default value."
|
155
|
-
return nil
|
156
|
-
end
|
159
|
+
end
|
157
160
|
|
158
|
-
|
161
|
+
def _table_exists?
|
162
|
+
table_exists?
|
163
|
+
rescue => e
|
164
|
+
false
|
159
165
|
end
|
160
166
|
|
161
167
|
def rails_initialized?
|
@@ -163,7 +169,7 @@ module RailsSettings
|
|
163
169
|
end
|
164
170
|
|
165
171
|
def _all_settings
|
166
|
-
|
172
|
+
RequestCache.settings ||= begin
|
167
173
|
Rails.cache.fetch(cache_key, expires_in: 1.week) do
|
168
174
|
vars = unscoped.select("var, value")
|
169
175
|
result = {}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module RailsSettings
|
2
|
+
if defined? ActiveSupport::CurrentAttributes
|
3
|
+
# For storage all settings in Current, it will reset after per request completed.
|
4
|
+
# Base on ActiveSupport::CurrentAttributes
|
5
|
+
# https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html
|
6
|
+
class RequestCache < ActiveSupport::CurrentAttributes
|
7
|
+
attribute :settings
|
8
|
+
end
|
9
|
+
else
|
10
|
+
# https://github.com/steveklabnik/request_store
|
11
|
+
# For Rails 5.0
|
12
|
+
require "request_store"
|
13
|
+
|
14
|
+
class RequestCache
|
15
|
+
class << self
|
16
|
+
def reset
|
17
|
+
self.settings = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def settings
|
21
|
+
RequestStore.store[:rails_settings_all_settings]
|
22
|
+
end
|
23
|
+
|
24
|
+
def settings=(val)
|
25
|
+
RequestStore.store[:rails_settings_all_settings]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails-settings-cached
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Lee
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-12-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -16,22 +16,22 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 5.0.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 5.0.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: codecov
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
|
-
type: :
|
34
|
+
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
@@ -39,7 +39,7 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: minitest
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
@@ -53,7 +53,7 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: pg
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
@@ -108,11 +108,11 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
-
description: "\n
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
111
|
+
description: "\n The best solution for store global settings in Rails applications.\n\n
|
112
|
+
\ This gem will managing a table of а global key, value pairs easy. Think of it
|
113
|
+
like a \n global Hash stored in your database, that uses simple ActiveRecord like
|
114
|
+
methods for manipulation.\n\n Keep track of any global setting that you dont want
|
115
|
+
to hard code into your rails app.\n You can store any kind of object. Strings,
|
116
116
|
numbers, arrays, or any object.\n "
|
117
117
|
email: huacnlee@gmail.com
|
118
118
|
executables: []
|
@@ -128,6 +128,7 @@ files:
|
|
128
128
|
- lib/rails-settings-cached.rb
|
129
129
|
- lib/rails-settings/base.rb
|
130
130
|
- lib/rails-settings/railtie.rb
|
131
|
+
- lib/rails-settings/request_cache.rb
|
131
132
|
- lib/rails-settings/version.rb
|
132
133
|
homepage: https://github.com/huacnlee/rails-settings-cached
|
133
134
|
licenses:
|
@@ -148,8 +149,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
148
149
|
- !ruby/object:Gem::Version
|
149
150
|
version: '0'
|
150
151
|
requirements: []
|
151
|
-
rubygems_version: 3.0
|
152
|
+
rubygems_version: 3.2.0
|
152
153
|
signing_key:
|
153
154
|
specification_version: 4
|
154
|
-
summary:
|
155
|
+
summary: The best solution for store global settings in Rails applications.
|
155
156
|
test_files: []
|