ef-config 1.4.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.
data/config.gemspec ADDED
@@ -0,0 +1,51 @@
1
+ $:.push File.expand_path('../lib', __FILE__)
2
+
3
+ # Maintain your gem's version:
4
+ require 'config/version'
5
+
6
+ # Describe your gem and declare its dependencies:
7
+ Gem::Specification.new do |s|
8
+ s.name = 'ef-config'
9
+ s.version = Config::VERSION
10
+ s.date = Time.now.strftime '%F'
11
+ s.authors = ['Hadley', 'Piotr Kuczynski', 'Fred Wu', 'Jacques Crocker']
12
+ s.email = %w(hadley@everfest.com)
13
+ s.summary = 'Effortless multi-environment settings in Rails, Sinatra, Pandrino and others'
14
+ s.description = 'Everfest fork of config gem'
15
+ s.homepage = 'https://bitbucket.org/everfest/config'
16
+ s.license = 'MIT'
17
+ s.extra_rdoc_files = %w(README.md CHANGELOG.md LICENSE.md)
18
+ s.rdoc_options = ['--charset=UTF-8']
19
+
20
+ s.files = `git ls-files`.split($/)
21
+ s.files.select! { |file| /(^lib\/|\.md$|\.gemspec$)/ =~ file }
22
+ s.files += Dir.glob('doc/**/*')
23
+
24
+ s.require_paths = ['lib']
25
+ s.required_ruby_version = '>= 2.0.0'
26
+
27
+ s.add_dependency 'activesupport', '>= 3.0'
28
+ s.add_dependency 'deep_merge', '~> 1.1.1'
29
+ s.add_dependency 'dry-validation', '~> 0.10.4' if RUBY_VERSION >= '2.1'
30
+
31
+ s.add_development_dependency 'bundler', '~> 1.13', '>= 1.13.6'
32
+ s.add_development_dependency 'rake', '~> 12.0', '>= 12.0.0'
33
+
34
+ # Testing
35
+ s.add_development_dependency 'appraisal', '~> 2.1', '>= 2.1.0'
36
+ s.add_development_dependency 'rails', '~> 5.0', '>= 5.0.1'
37
+ s.add_development_dependency 'rspec', '~> 3.5', '>= 3.5.0'
38
+ s.add_development_dependency 'rspec-rails', '~> 3.5', '>= 3.5.2'
39
+ s.add_development_dependency 'test-unit', '~> 3.2', '>= 3.2.1'
40
+ s.add_development_dependency 'sqlite3', '~> 1.3', '>= 1.3.11'
41
+ s.add_development_dependency 'pry'
42
+
43
+ # Static code analysis
44
+ s.add_development_dependency 'mdl', '~> 0.4', '>= 0.4.0'
45
+ s.add_development_dependency 'rubocop', '~> 0.46', '>= 0.46.0'
46
+
47
+ if ENV['TRAVIS']
48
+ s.add_development_dependency 'simplecov', '~> 0.12.0'
49
+ s.add_development_dependency 'codeclimate-test-reporter', '~> 1.0.3'
50
+ end
51
+ end
data/lib/config.rb ADDED
@@ -0,0 +1,107 @@
1
+ require 'active_support/core_ext/module/attribute_accessors'
2
+
3
+ require 'config/compatibility'
4
+ require 'config/options'
5
+ require 'config/version'
6
+ require 'config/integrations/rails/engine' if defined?(::Rails)
7
+ require 'config/sources/yaml_source'
8
+ require 'config/sources/hash_source'
9
+ require 'config/validation/schema' if RUBY_VERSION >= '2.1'
10
+ require 'deep_merge'
11
+
12
+ module Config
13
+ extend Config::Validation::Schema if RUBY_VERSION >= '2.1'
14
+
15
+ # Ensures the setup only gets run once
16
+ @@_ran_once = false
17
+
18
+ mattr_accessor :const_name, :use_env, :env_prefix, :env_separator, :env_converter, :env_parse_values
19
+ @@const_name = 'Settings'
20
+ @@use_env = false
21
+ @@env_prefix = @@const_name
22
+ @@env_separator = '.'
23
+ @@env_converter = :downcase
24
+ @@env_parse_values = true
25
+
26
+ # deep_merge options
27
+ mattr_accessor :knockout_prefix, :overwrite_arrays
28
+ @@knockout_prefix = nil
29
+ @@overwrite_arrays = true
30
+
31
+ def self.setup
32
+ yield self if @@_ran_once == false
33
+ @@_ran_once = true
34
+ end
35
+
36
+ # Create a populated Options instance from a settings file. If a second file is given, then the sections of that
37
+ # file will overwrite existing sections of the first file.
38
+ def self.load_files(*files)
39
+ config = Options.new
40
+
41
+ # add settings sources
42
+ [files].flatten.compact.uniq.each do |file|
43
+ config.add_source!(file.to_s)
44
+ end
45
+
46
+ config.load!
47
+ config.load_env! if @@use_env
48
+ config
49
+ end
50
+
51
+ # Loads and sets the settings constant!
52
+ def self.load_and_set_settings(*files)
53
+ Kernel.send(:remove_const, Config.const_name) if Kernel.const_defined?(Config.const_name)
54
+ Kernel.const_set(Config.const_name, Config.load_files(files))
55
+ end
56
+
57
+ def self.load_and_set_nested_settings(base_path, default_file_name)
58
+ Kernel.send(:remove_const, Config.const_name) if Kernel.const_defined?(Config.const_name)
59
+
60
+ config = Options.new
61
+ config.add_source!(File.join(base_path, default_file_name).to_s)
62
+ #config.add_source!(File.join(base_path, priority_file_name).to_s)
63
+
64
+ folder_names = Dir.glob("#{base_path}/**/**").select {|f| File.directory? f}
65
+
66
+ folder_names.each do |folder_name|
67
+ namespace_path = folder_name.to_s.sub("#{base_path.to_s}/", "")
68
+ namespaces = namespace_path.split("/")
69
+
70
+ namespaces.each_with_index do |namespace, i|
71
+ path = namespaces.first(i + 1).join("/")
72
+ namespaced_default_file = File.join(base_path, path, default_file_name).to_s
73
+ #priority_default_file = File.join(base_path, path, priority_file_name).to_s
74
+
75
+ config.add_source!(namespaced_default_file.to_s, path)
76
+ #config.add_source!(priority_default_file.to_s, path)
77
+ end
78
+ end
79
+
80
+ config.load!
81
+ config.load_env! if @@use_env
82
+
83
+ Kernel.const_set(Config.const_name, config)
84
+ end
85
+
86
+ def self.setting_files(config_root, env)
87
+ [
88
+ File.join(config_root, "settings.yml").to_s,
89
+ File.join(config_root, "settings", "#{env}.yml").to_s,
90
+ File.join(config_root, "environments", "#{env}.yml").to_s,
91
+
92
+ File.join(config_root, "settings.local.yml").to_s,
93
+ File.join(config_root, "settings", "#{env}.local.yml").to_s,
94
+ File.join(config_root, "environments", "#{env}.local.yml").to_s
95
+ ].freeze
96
+ end
97
+
98
+ def self.reload!
99
+ Kernel.const_get(Config.const_name).reload!
100
+ end
101
+ end
102
+
103
+ # Rails integration
104
+ require('config/integrations/rails/railtie') if defined?(::Rails)
105
+
106
+ # Sinatra integration
107
+ require('config/integrations/sinatra') if defined?(::Sinatra)
@@ -0,0 +1,3 @@
1
+ if defined?(RbConfig) && defined?(Config)
2
+ Object.send :remove_const, :Config
3
+ end
@@ -0,0 +1,59 @@
1
+ require 'bundler'
2
+
3
+ module Config
4
+ module Integrations
5
+ class Heroku < Struct.new(:app)
6
+ def invoke
7
+ puts 'Setting vars...'
8
+ heroku_command = "config:set #{vars}"
9
+ heroku(heroku_command)
10
+ puts 'Vars set:'
11
+ puts heroku_command
12
+ end
13
+
14
+ def vars
15
+ # Load only local options to Heroku
16
+ Config.load_and_set_settings(
17
+ Rails.root.join("config", "settings.local.yml").to_s,
18
+ Rails.root.join("config", "settings", "#{environment}.local.yml").to_s,
19
+ Rails.root.join("config", "environments", "#{environment}.local.yml").to_s
20
+ )
21
+
22
+ out = ''
23
+ dotted_hash = to_dotted_hash Kernel.const_get(Config.const_name).to_hash, {}, Config.const_name
24
+ dotted_hash.each {|key, value| out += " #{key}=#{value} "}
25
+ out
26
+ end
27
+
28
+ def environment
29
+ heroku("run 'echo $RAILS_ENV'").chomp[/(\w+)\z/]
30
+ end
31
+
32
+ def heroku(command)
33
+ with_app = app ? " --app #{app}" : ""
34
+ `heroku #{command}#{with_app}`
35
+ end
36
+
37
+ def `(command)
38
+ Bundler.with_clean_env { super }
39
+ end
40
+
41
+ def to_dotted_hash(source, target = {}, namespace = nil)
42
+ prefix = "#{namespace}." if namespace
43
+ case source
44
+ when Hash
45
+ source.each do |key, value|
46
+ to_dotted_hash(value, target, "#{prefix}#{key}")
47
+ end
48
+ when Array
49
+ source.each_with_index do |value, index|
50
+ to_dotted_hash(value, target, "#{prefix}#{index}")
51
+ end
52
+ else
53
+ target[namespace] = source
54
+ end
55
+ target
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,9 @@
1
+ module Config
2
+ module Integrations
3
+ module Rails
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace Config
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,38 @@
1
+ module Config
2
+ module Integrations
3
+ module Rails
4
+ class Railtie < ::Rails::Railtie
5
+ def preload
6
+ # Manually load the custom initializer before everything else
7
+ initializer = ::Rails.root.join('config', 'initializers', 'config.rb')
8
+ require initializer if File.exist?(initializer)
9
+
10
+ # Parse the settings before any of the initializers
11
+ Config.load_and_set_settings(
12
+ Config.setting_files(::Rails.root.join('config'), ::Rails.env)
13
+ )
14
+ end
15
+
16
+ # Load rake tasks (eg. Heroku)
17
+ rake_tasks do
18
+ Dir[File.join(File.dirname(__FILE__), '../tasks/*.rake')].each { |f| load f }
19
+ end
20
+
21
+ config.before_configuration { preload }
22
+
23
+ # Development environment should reload settings on every request
24
+ if ::Rails.env.development?
25
+ initializer :config_reload_on_development do
26
+ ActionController::Base.class_eval do
27
+ if ::Rails::VERSION::MAJOR >= 4
28
+ prepend_before_action { ::Config.reload! }
29
+ else
30
+ prepend_before_filter { ::Config.reload! }
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,26 @@
1
+ require "config/rack/reloader"
2
+
3
+ module Config
4
+ # provide helper to register within your Sinatra app
5
+ #
6
+ # set :root, File.dirname(__FILE__)
7
+ # register Config
8
+ #
9
+ def self.registered(app)
10
+ app.configure do |inner_app|
11
+
12
+ env = inner_app.environment || ENV["RACK_ENV"]
13
+ root = inner_app.root
14
+
15
+ # use Padrino settings if applicable
16
+ if defined?(Padrino)
17
+ env = Padrino.env
18
+ root = Padrino.root
19
+ end
20
+
21
+ Config.load_and_set_settings(Config.setting_files(File.join(root, 'config'), env))
22
+
23
+ inner_app.use(::Config::Rack::Reloader) if inner_app.development?
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,191 @@
1
+ require 'ostruct'
2
+ require 'config/validation/validate' if RUBY_VERSION >= '2.1'
3
+
4
+ module Config
5
+ class Options < OpenStruct
6
+ include Enumerable
7
+ include Validation::Validate if RUBY_VERSION >= '2.1'
8
+
9
+ def keys
10
+ marshal_dump.keys
11
+ end
12
+
13
+ def empty?
14
+ marshal_dump.empty?
15
+ end
16
+
17
+ def add_source!(source, namespace=nil)
18
+ # handle yaml file paths
19
+ source = (Sources::YAMLSource.new(source, namespace)) if source.is_a?(String)
20
+ source = (Sources::HashSource.new(source, namespace)) if source.is_a?(Hash)
21
+
22
+ @config_sources ||= []
23
+ @config_sources << source
24
+ end
25
+
26
+ def prepend_source!(source)
27
+ source = (Sources::YAMLSource.new(source)) if source.is_a?(String)
28
+ source = (Sources::HashSource.new(source)) if source.is_a?(Hash)
29
+
30
+ @config_sources ||= []
31
+ @config_sources.unshift(source)
32
+ end
33
+
34
+ def reload_env!
35
+ return self if ENV.nil? || ENV.empty?
36
+
37
+ hash = Hash.new
38
+
39
+ ENV.each do |variable, value|
40
+ keys = variable.to_s.split(Config.env_separator)
41
+
42
+ next if keys.shift != (Config.env_prefix || Config.const_name)
43
+
44
+ keys.map! { |key|
45
+ case Config.env_converter
46
+ when :downcase then
47
+ key.downcase.to_sym
48
+ when nil then
49
+ key.to_sym
50
+ else
51
+ raise "Invalid ENV variables name converter: #{Config.env_converter}"
52
+ end
53
+ }
54
+
55
+ leaf = keys[0...-1].inject(hash) { |h, key|
56
+ h[key] ||= {}
57
+ }
58
+
59
+ leaf[keys.last] = Config.env_parse_values ? __value(value) : value
60
+ end
61
+
62
+ merge!(hash)
63
+ end
64
+
65
+ alias :load_env! :reload_env!
66
+
67
+ # look through all our sources and rebuild the configuration
68
+ def reload!
69
+ conf = {}
70
+ @config_sources.each do |source|
71
+ source_conf = source.load
72
+
73
+ if conf.empty?
74
+ conf = source_conf
75
+ else
76
+ DeepMerge.deep_merge!(source_conf,
77
+ conf,
78
+ preserve_unmergeables: false,
79
+ knockout_prefix: Config.knockout_prefix,
80
+ overwrite_arrays: Config.overwrite_arrays)
81
+ end
82
+ end
83
+
84
+ # swap out the contents of the OStruct with a hash (need to recursively convert)
85
+ marshal_load(__convert(conf).marshal_dump)
86
+
87
+ reload_env! if Config.use_env
88
+ validate! if RUBY_VERSION >= '2.1'
89
+
90
+ self
91
+ end
92
+
93
+ alias :load! :reload!
94
+
95
+ def reload_from_files(*files)
96
+ Config.load_and_set_settings(files)
97
+ reload!
98
+ end
99
+
100
+ def to_hash
101
+ result = {}
102
+ marshal_dump.each do |k, v|
103
+ if v.instance_of? Config::Options
104
+ result[k] = v.to_hash
105
+ elsif v.instance_of? Array
106
+ result[k] = descend_array(v)
107
+ else
108
+ result[k] = v
109
+ end
110
+ end
111
+ result
112
+ end
113
+
114
+ def each(*args, &block)
115
+ marshal_dump.each(*args, &block)
116
+ end
117
+
118
+ def to_json(*args)
119
+ require "json" unless defined?(JSON)
120
+ to_hash.to_json(*args)
121
+ end
122
+
123
+ def merge!(hash)
124
+ current = to_hash
125
+ DeepMerge.deep_merge!(hash.dup,
126
+ current,
127
+ preserve_unmergeables: false,
128
+ overwrite_arrays: Config.overwrite_arrays)
129
+ marshal_load(__convert(current).marshal_dump)
130
+ self
131
+ end
132
+
133
+ # Some keywords that don't play nicely with OpenStruct
134
+ SETTINGS_RESERVED_NAMES = %w{select collect test}
135
+
136
+ # An alternative mechanism for property access.
137
+ # This let's you do foo['bar'] along with foo.bar.
138
+ def [](param)
139
+ return super if SETTINGS_RESERVED_NAMES.include?(param)
140
+ send("#{param}")
141
+ end
142
+
143
+ def []=(param, value)
144
+ send("#{param}=", value)
145
+ end
146
+
147
+ SETTINGS_RESERVED_NAMES.each do |name|
148
+ define_method name do
149
+ self[name]
150
+ end
151
+ end
152
+
153
+ protected
154
+
155
+ def descend_array(array)
156
+ array.map do |value|
157
+ if value.instance_of? Config::Options
158
+ value.to_hash
159
+ elsif value.instance_of? Array
160
+ descend_array(value)
161
+ else
162
+ value
163
+ end
164
+ end
165
+ end
166
+
167
+ # Recursively converts Hashes to Options (including Hashes inside Arrays)
168
+ def __convert(h) #:nodoc:
169
+ s = self.class.new
170
+
171
+ h.each do |k, v|
172
+ k = k.to_s if !k.respond_to?(:to_sym) && k.respond_to?(:to_s)
173
+ s.new_ostruct_member(k)
174
+
175
+ if v.is_a?(Hash)
176
+ v = v["type"] == "hash" ? v["contents"] : __convert(v)
177
+ elsif v.is_a?(Array)
178
+ v = v.collect { |e| e.instance_of?(Hash) ? __convert(e) : e }
179
+ end
180
+
181
+ s.send("#{k}=".to_sym, v)
182
+ end
183
+ s
184
+ end
185
+
186
+ # Try to convert string to a correct type
187
+ def __value(v)
188
+ Integer(v) rescue Float(v) rescue v
189
+ end
190
+ end
191
+ end