ef-config 1.4.1

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