ultra_settings 0.0.1.rc1 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +321 -2
- data/VERSION +1 -1
- data/app/application.css +85 -0
- data/app/application.js +19 -0
- data/app/index.html.erb +93 -0
- data/app/layout.css +27 -0
- data/app/layout.html.erb +21 -0
- data/lib/ultra_settings/coerce.rb +98 -0
- data/lib/ultra_settings/configuration.rb +379 -79
- data/lib/ultra_settings/field.rb +57 -88
- data/lib/ultra_settings/rack_app.rb +25 -0
- data/lib/ultra_settings/railtie.rb +33 -0
- data/lib/ultra_settings/version.rb +5 -0
- data/lib/ultra_settings/web_view.rb +38 -0
- data/lib/ultra_settings/yaml_config.rb +78 -0
- data/lib/ultra_settings.rb +168 -74
- data/ultra_settings.gemspec +3 -4
- metadata +18 -35
data/lib/ultra_settings/field.rb
CHANGED
@@ -5,51 +5,41 @@ module UltraSettings
|
|
5
5
|
class Field
|
6
6
|
attr_reader :name
|
7
7
|
attr_reader :type
|
8
|
+
attr_reader :description
|
8
9
|
attr_reader :default
|
9
10
|
attr_reader :default_if
|
10
11
|
attr_reader :env_var
|
11
|
-
attr_reader :
|
12
|
+
attr_reader :runtime_setting
|
12
13
|
attr_reader :yaml_key
|
13
|
-
attr_reader :env_var_prefix
|
14
|
-
attr_reader :env_var_upcase
|
15
|
-
attr_reader :setting_prefix
|
16
|
-
attr_reader :setting_upcase
|
17
14
|
|
18
15
|
# @param name [String, Symbol] The name of the field.
|
19
16
|
# @param type [Symbol] The type of the field.
|
17
|
+
# @param description [String] The description of the field.
|
20
18
|
# @param default [Object] The default value of the field.
|
21
19
|
# @param default_if [Proc] A proc that returns true if the default value should be used.
|
22
20
|
# @param env_var [String, Symbol] The name of the environment variable to use for the field.
|
23
|
-
# @param
|
21
|
+
# @param runtime_setting [String, Symbol] The name of the setting to use for the field.
|
24
22
|
# @param yaml_key [String, Symbol] The name of the YAML key to use for the field.
|
25
|
-
# @param env_var_prefix [String, Symbol] The prefix to use for the environment variable name.
|
26
|
-
# @param env_var_upcase [Boolean] Whether or not to upcase the environment variable name.
|
27
|
-
# @param setting_prefix [String, Symbol] The prefix to use for the setting name.
|
28
|
-
# @param setting_upcase [Boolean] Whether or not to upcase the setting name.
|
29
23
|
def initialize(
|
30
24
|
name:,
|
31
25
|
type: :string,
|
26
|
+
description: nil,
|
32
27
|
default: nil,
|
33
28
|
default_if: nil,
|
34
29
|
env_var: nil,
|
35
|
-
|
30
|
+
runtime_setting: nil,
|
36
31
|
yaml_key: nil,
|
37
|
-
|
38
|
-
env_var_upcase: true,
|
39
|
-
setting_prefix: nil,
|
40
|
-
setting_upcase: false
|
32
|
+
static: false
|
41
33
|
)
|
42
|
-
@name =
|
34
|
+
@name = name.to_s.freeze
|
43
35
|
@type = type.to_sym
|
44
|
-
@
|
36
|
+
@description = description&.to_s&.freeze
|
37
|
+
@default = Coerce.coerce_value(default, @type).freeze
|
45
38
|
@default_if = default_if
|
46
|
-
@env_var =
|
47
|
-
@
|
48
|
-
@yaml_key =
|
49
|
-
@
|
50
|
-
@env_var_upcase = !!env_var_upcase
|
51
|
-
@setting_prefix = frozen_string(setting_prefix)
|
52
|
-
@setting_upcase = !!setting_upcase
|
39
|
+
@env_var = env_var&.to_s&.freeze
|
40
|
+
@runtime_setting = runtime_setting&.to_s&.freeze
|
41
|
+
@yaml_key = yaml_key&.to_s&.freeze
|
42
|
+
@static = !!static
|
53
43
|
end
|
54
44
|
|
55
45
|
# Get the value for the field from the passed in state.
|
@@ -58,85 +48,64 @@ module UltraSettings
|
|
58
48
|
# @param settings [#[]] The runtime settings.
|
59
49
|
# @param yaml_config [#[]] The YAML configuration.
|
60
50
|
def value(env: nil, settings: nil, yaml_config: nil)
|
61
|
-
|
62
|
-
val = coerce_value(val).freeze
|
63
|
-
val = @default if use_default?(val)
|
64
|
-
val
|
51
|
+
fetch_value_and_source(env: env, settings: settings, yaml_config: yaml_config).first
|
65
52
|
end
|
66
53
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
end
|
54
|
+
# Get the source for the field from the passed in state.
|
55
|
+
#
|
56
|
+
# @param env [Hash] The environment variables.
|
57
|
+
# @param settings [Hash] The runtime settings.
|
58
|
+
# @param yaml_config [Hash] The YAML configuration.
|
59
|
+
# @return [Symbol, nil] The source of the value (:env, :settings, or :yaml).
|
60
|
+
def source(env: nil, settings: nil, yaml_config: nil)
|
61
|
+
fetch_value_and_source(env: env, settings: settings, yaml_config: yaml_config).last
|
62
|
+
end
|
77
63
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
64
|
+
# Coerce the passed in value to the type of the field.
|
65
|
+
#
|
66
|
+
# @param value [Object] The value to coerce.
|
67
|
+
# @return [Object] The coerced value.
|
68
|
+
def coerce(value)
|
69
|
+
Coerce.coerce_value(value, @type)
|
70
|
+
end
|
82
71
|
|
83
|
-
|
72
|
+
# Returns true if the field is static.
|
73
|
+
#
|
74
|
+
# @return [Boolean]
|
75
|
+
def static?
|
76
|
+
@static
|
84
77
|
end
|
85
78
|
|
86
|
-
|
87
|
-
|
79
|
+
private
|
80
|
+
|
81
|
+
def fetch_value_and_source(env:, settings:, yaml_config:)
|
82
|
+
source = nil
|
88
83
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
value
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
value.to_s.to_sym
|
84
|
+
value = env[env_var] if env && env_var
|
85
|
+
value = nil if value == ""
|
86
|
+
if value.nil?
|
87
|
+
value = settings[runtime_setting] if settings && runtime_setting
|
88
|
+
value = nil if value == ""
|
89
|
+
if value.nil?
|
90
|
+
value = yaml_value(yaml_config)
|
91
|
+
value = nil if value == ""
|
92
|
+
source = :yaml unless value.nil?
|
93
|
+
else
|
94
|
+
source = :settings
|
95
|
+
end
|
102
96
|
else
|
103
|
-
|
97
|
+
source = :env
|
104
98
|
end
|
105
|
-
end
|
106
99
|
|
107
|
-
|
108
|
-
var_name = env_var
|
109
|
-
if var_name.nil?
|
110
|
-
var_name = "#{env_var_prefix}#{name}"
|
111
|
-
var_name = var_name.upcase if env_var_upcase
|
112
|
-
end
|
113
|
-
env[var_name.to_s]
|
114
|
-
end
|
100
|
+
value = coerce(value).freeze
|
115
101
|
|
116
|
-
|
117
|
-
var_name = setting_name
|
118
|
-
if var_name.nil?
|
119
|
-
var_name = "#{setting_prefix}#{name}"
|
120
|
-
var_name = var_name.upcase if setting_upcase
|
121
|
-
end
|
122
|
-
settings[var_name.to_s]
|
102
|
+
[value, source]
|
123
103
|
end
|
124
104
|
|
125
105
|
def yaml_value(yaml_config)
|
126
|
-
|
127
|
-
yaml_config[key.to_s]
|
128
|
-
end
|
129
|
-
|
130
|
-
def use_default?(value)
|
131
|
-
if value && @default_if
|
132
|
-
@default_if.call(value)
|
133
|
-
else
|
134
|
-
value.nil?
|
135
|
-
end
|
136
|
-
end
|
106
|
+
return nil unless yaml_config && yaml_key
|
137
107
|
|
138
|
-
|
139
|
-
value&.to_s&.dup&.freeze
|
108
|
+
yaml_config[yaml_key]
|
140
109
|
end
|
141
110
|
end
|
142
111
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module UltraSettings
|
4
|
+
# Rack application for displaying the current settings in an HTML page.
|
5
|
+
# No setting values are displayed, but you should still add some
|
6
|
+
# sort of authentication if you want to use this in production.
|
7
|
+
class RackApp
|
8
|
+
def initialize
|
9
|
+
@webview = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
[200, {"content-type" => "text/html; charset=utf8"}, [webview.render_settings]]
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def webview
|
19
|
+
if ENV.fetch("RAILS_ENV", ENV.fetch("RACK_ENV", "development")) == "development"
|
20
|
+
@webview = nil
|
21
|
+
end
|
22
|
+
@webview ||= WebView.new
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module UltraSettings
|
4
|
+
# Railtie to automatically configure settings for Rails applications.
|
5
|
+
# By default this will automatically load any configuration classes in the
|
6
|
+
# app/configurations directory. This can be customized by setting the
|
7
|
+
# `config.ultra_settings.auto_load_directories` option.
|
8
|
+
class Railtie < Rails::Railtie
|
9
|
+
config.ultra_settings = ActiveSupport::OrderedOptions.new
|
10
|
+
config.ultra_settings.auto_load_directories = [File.join("app", "configurations")]
|
11
|
+
|
12
|
+
config.before_configuration do
|
13
|
+
UltraSettings.yaml_config_env = Rails.env
|
14
|
+
UltraSettings.yaml_config_path = Rails.root.join("config")
|
15
|
+
end
|
16
|
+
|
17
|
+
# Automatically register any configuration classes in the app/configurations
|
18
|
+
# directory. The path to load can be customized by setting the
|
19
|
+
# `config.ultra_settings.auto_load_directory` option.
|
20
|
+
config.after_initialize do
|
21
|
+
Array(Rails.application.config.ultra_settings.auto_load_directories).each do |directory|
|
22
|
+
next unless directory
|
23
|
+
|
24
|
+
app_config_dir = Rails.root.join(directory)
|
25
|
+
app_config_dir.glob("**/*_configuration.rb").each do |file_path|
|
26
|
+
config_name = file_path.basename("_configuration.rb")
|
27
|
+
class_name = file_path.relative_path_from(app_config_dir).to_s.chomp(".rb").classify
|
28
|
+
UltraSettings.add(config_name, class_name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module UltraSettings
|
4
|
+
# Helper class for rendering the settings information in an HTML page.
|
5
|
+
class WebView
|
6
|
+
attr_reader :css
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@index_template = erb_template("index.html.erb")
|
10
|
+
@layout_template = erb_template("layout.html.erb")
|
11
|
+
@layout_css = read_app_file("layout.css")
|
12
|
+
@css = read_app_file("application.css")
|
13
|
+
@javascript = read_app_file("application.js")
|
14
|
+
end
|
15
|
+
|
16
|
+
def render_settings
|
17
|
+
@layout_template.result(binding)
|
18
|
+
end
|
19
|
+
|
20
|
+
def content
|
21
|
+
@index_template.result(binding)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def erb_template(path)
|
27
|
+
ERB.new(read_app_file(path))
|
28
|
+
end
|
29
|
+
|
30
|
+
def read_app_file(path)
|
31
|
+
File.read(File.join(app_dir, path))
|
32
|
+
end
|
33
|
+
|
34
|
+
def app_dir
|
35
|
+
File.expand_path(File.join("..", "..", "app"), __dir__)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module UltraSettings
|
4
|
+
# Helper class to load YAML configuration files. Any ERB markup in the YAML
|
5
|
+
# file will be evaluated. The YAML file should be structured like this:
|
6
|
+
#
|
7
|
+
# ```yaml
|
8
|
+
# shared:
|
9
|
+
# foo: bar
|
10
|
+
# bar: baz
|
11
|
+
#
|
12
|
+
# development:
|
13
|
+
# bar: qux
|
14
|
+
# biz: buz
|
15
|
+
#
|
16
|
+
# test:
|
17
|
+
# bar: qix
|
18
|
+
# biz: biz
|
19
|
+
# ```
|
20
|
+
#
|
21
|
+
# The section with the key matching the environment name is merged into
|
22
|
+
# the shared section. In this example, the development environment would
|
23
|
+
# have the following configuration:
|
24
|
+
#
|
25
|
+
# ```ruby
|
26
|
+
# {
|
27
|
+
# "foo" => "bar",
|
28
|
+
# "bar" => "qux",
|
29
|
+
# "biz" => "buz"
|
30
|
+
# }
|
31
|
+
# ```
|
32
|
+
#
|
33
|
+
# In addition, the keys are flattened into a one level deep hash with dots
|
34
|
+
# separating the keys.
|
35
|
+
class YamlConfig
|
36
|
+
def initialize(path, environment)
|
37
|
+
yaml = load_yaml(path)
|
38
|
+
@config = environment_config(yaml, environment)
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_h
|
42
|
+
@config
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def load_yaml(path)
|
48
|
+
yaml = File.read(path)
|
49
|
+
|
50
|
+
if yaml.include?("<%")
|
51
|
+
yaml = ERB.new(yaml).result
|
52
|
+
end
|
53
|
+
|
54
|
+
hash = YAML.load(yaml) # rubocop:disable Security/YAMLLoad
|
55
|
+
hash = {} unless hash.is_a?(Hash)
|
56
|
+
hash
|
57
|
+
end
|
58
|
+
|
59
|
+
def environment_config(yaml, environment)
|
60
|
+
shared = flatten_hash(yaml.fetch("shared", {}))
|
61
|
+
env = flatten_hash(yaml.fetch(environment, {}))
|
62
|
+
shared.merge(env)
|
63
|
+
end
|
64
|
+
|
65
|
+
def flatten_hash(hash, prefix = nil)
|
66
|
+
hash.each_with_object({}) do |(key, value), result|
|
67
|
+
key = key.to_s
|
68
|
+
key = "#{prefix}.#{key}" if prefix
|
69
|
+
|
70
|
+
if value.is_a?(Hash)
|
71
|
+
result.merge!(flatten_hash(value, key))
|
72
|
+
else
|
73
|
+
result[key] = value
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|