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.
@@ -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 :setting_name
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 setting_name [String, Symbol] The name of the setting to use for the field.
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
- setting_name: nil,
30
+ runtime_setting: nil,
36
31
  yaml_key: nil,
37
- env_var_prefix: nil,
38
- env_var_upcase: true,
39
- setting_prefix: nil,
40
- setting_upcase: false
32
+ static: false
41
33
  )
42
- @name = frozen_string(name)
34
+ @name = name.to_s.freeze
43
35
  @type = type.to_sym
44
- @default = coerce_value(default).freeze
36
+ @description = description&.to_s&.freeze
37
+ @default = Coerce.coerce_value(default, @type).freeze
45
38
  @default_if = default_if
46
- @env_var = frozen_string(env_var)
47
- @setting_name = frozen_string(setting_name)
48
- @yaml_key = frozen_string(yaml_key)
49
- @env_var_prefix = frozen_string(env_var_prefix)
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
- val = fetch_value(env: env, settings: settings, yaml_config: yaml_config)
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
- private
68
-
69
- def fetch_value(env:, settings:, yaml_config:)
70
- value = env_value(env) if env
71
- value = nil if value == ""
72
-
73
- if value.nil? && settings
74
- value = runtime_value(settings)
75
- value = nil if value == ""
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
- if value.nil? && yaml_config
79
- value = yaml_value(yaml_config)
80
- value = nil if value == ""
81
- end
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
- value
72
+ # Returns true if the field is static.
73
+ #
74
+ # @return [Boolean]
75
+ def static?
76
+ @static
84
77
  end
85
78
 
86
- def coerce_value(value)
87
- return nil if value.nil?
79
+ private
80
+
81
+ def fetch_value_and_source(env:, settings:, yaml_config:)
82
+ source = nil
88
83
 
89
- case type
90
- when :integer
91
- value.is_a?(Integer) ? value : value.to_s&.to_i
92
- when :float
93
- value.is_a?(Float) ? value : value.to_s&.to_f
94
- when :boolean
95
- SuperSettings::Coerce.boolean(value)
96
- when :datetime
97
- SuperSettings::Coerce.time(value)
98
- when :array
99
- Array(value).map(&:to_s)
100
- when :symbol
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
- value.to_s
97
+ source = :env
104
98
  end
105
- end
106
99
 
107
- def env_value(env)
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
- def runtime_value(settings)
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
- key = (yaml_key || name)
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
- def frozen_string(value)
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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UltraSettings
4
+ VERSION = File.read(File.join(__dir__, "..", "..", "VERSION")).strip
5
+ 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