ultra_settings 0.0.1.rc1 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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