appconf 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,119 @@
1
+ # AppConf
2
+
3
+ **AppConf** is a ruby gem used to query values of hierarchically organized configuration items from a series of configuration YAML files in priority, via dot split item path names.
4
+
5
+
6
+
7
+ ## Installation
8
+
9
+ ```shell
10
+ gem install appconf
11
+ ```
12
+
13
+ ## Description
14
+
15
+ **AppConf** loads and parses a series of YAML configuration files in priority, which means if multiple configuration items with the same path name exist in different files, then the value of the item in the highest priority file will be selected as query result.
16
+
17
+ **AppConf** will loads all these YAML configuration files, if exist, in priority highest to lowest:
18
+
19
+ - *`/path/to/specified/deployment/dir`*/*`myapp`*.*`ruby-platform`*.yaml
20
+ - *`/path/to/specified/deployment/dir`*/*`myapp`*.yaml
21
+ - *`/home/username`*/*`.myapp`*/*`myapp`*.*`ruby-platform`*.yaml
22
+ - *`/home/username`*/*`.myapp`*/*`myapp`*.yaml
23
+ - /etc/*`myapp`*/*`myapp`*.*`ruby-platform`*.yaml
24
+ - /etc/*`myapp`*/*`myapp`*.yaml
25
+ - *`/path/to/myapp`*/conf/*`myapp`*.*`ruby-platform`*.yaml
26
+ - *`/path/to/myapp`*/conf/*`myapp`*.yaml
27
+
28
+ Where:
29
+
30
+ - *`/path/to/specified/deployment/dir`*: The directory where you app is deployed, use AppConfig::setup to tell the gem where it is.
31
+
32
+
33
+ - *`myapp`*: The name of the configuration file you requested via AppConfig::get_conf, or the name of your app when nil is used for configuration file name. The app name is setup via AppConfig::setup.
34
+ - *`.myapp`*: Note that there is a **dot** before `myapp` for the name of this directory, which holds user level configuration file. While on Windows, there is **no** such dot.
35
+
36
+
37
+ - *`ruby-platform`*: The platform of the ruby is running on. For jruby, it is `java`.
38
+
39
+
40
+ - *`/home/username`*: The home directory of the user who is running your app. In Windows, it's `ENV['USERPROFILE']`, for POSIX, `ENV['HOME']` is used.
41
+
42
+
43
+ - `/etc`: In Windows, `ENV['ALLUSERSPROFILE']` is used.
44
+
45
+
46
+ - *`/path/to/myapp`*: The root directory where your app resides in.
47
+
48
+ ## Why YAML?
49
+
50
+ XML is heavy and verbose, JSON has no comments, that's it.
51
+
52
+ ## Usage
53
+
54
+ Say you have a ruby app named `awsome`, need a default configuration file located in `conf` directory under your app's root path, named `awsome.yaml` like this (in YAML):
55
+
56
+ ```yaml
57
+ ---
58
+ db:
59
+ driver: sequel
60
+ conection: sqlite://awsome.db
61
+ ```
62
+
63
+ While when you are testing you app on your development machine, you want to use a specific sqlite database file located at `/home/arloan/awsome-db/test-2016.db`, how can I do? It's simple, place a file named `awsome.yaml` under `/home/arloan/.awsome/`, with the contents:
64
+
65
+ ```yaml
66
+ ---
67
+ db:
68
+ connection: sqlite:///home/arloan/awsome-db/test-2016.db
69
+ ```
70
+
71
+ In addition, you want your app to be compatible with jruby, and want to use a different, jruby specific database connection method when running under jruby, then you can create a configuration file named `awsome.java.yaml` and place it along with your default `awsome.yaml`, in `conf` directory under your app's root path, with the contents:
72
+
73
+ ```yaml
74
+ ---
75
+ db:
76
+ connection: jdbc:sqlite:awsome.db
77
+ ```
78
+
79
+ In your app's code, you write:
80
+
81
+ ```ruby
82
+ require 'appconf'
83
+
84
+ # in app's initialization part
85
+ AppConfig.setup 'Awsome', # app name, will be converted to lower case automatically
86
+ File.dirname(__FILE__), # app root dir path
87
+ '/path/to/app/deployment/directory' # app deployment path, i.e production env
88
+
89
+ # default config with app name and deployment directory
90
+ default_config = AppConfig.get_config
91
+
92
+ # use another series of configuration files with new name, use 'new-name.yaml' series
93
+ another_config = AppConfig.get_config 'new-name'
94
+
95
+ # you can overwrite deployment directory when get a config serie
96
+ overwritten_config = AppConfig.get_config nil, '/path/to/new/deployment/dir'
97
+
98
+ # to query config value:
99
+ # conn == 'sqlite://awsome.db' in default case
100
+ # conn == 'sqlite:///home/arloan/awsome-db/test-2016.db' in development environment.
101
+ # conn == 'jdbc:sqlite:awsome.db' in jruby
102
+ conn = default_config['db.connection']
103
+
104
+ # non exist path returns nil, no exception
105
+ this_is_nil = default_config['non.exist.conf.item.path']
106
+ ```
107
+
108
+ ## License
109
+
110
+ (The MIT License)
111
+
112
+ Copyright © Arloan Bone (arloan@gmail.com)
113
+
114
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
115
+
116
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
117
+
118
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
119
+
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- encoding: utf-8 -*-
3
+
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs << %w{ test }
8
+ t.pattern = 'test/test*.rb'
9
+ end
10
+
11
+ task :default => :test
@@ -0,0 +1,199 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # author: Arloan Bone (arloan@gmail.com)
3
+
4
+ require 'yaml'
5
+
6
+ ##usage:
7
+ # AppConfig.setup('myApp', 'app/root/dir', 'deploy/config/dir')
8
+ # #'app_root_dir' determines where distribute-default config resides
9
+ # #'myApp' will be converted to lowercase automatically.
10
+ # #'deploy_config_dir': config file residential dir on deployment, can be nil,
11
+ # # in which case, config dir from environment MYAPP_CONFIG_DIR will be used.
12
+ # config = AppConfig::get_config('config_name', 'config/dir')
13
+ # #'config_name' can be nil, in which case myapp.config.yaml is used.
14
+ # #'config/dir' non-nil to override deploy config dir from AppConfig::setup
15
+ ##to query config item:
16
+ # config['some.settings.item.dotted.path']
17
+
18
+ class Hash
19
+ def path_lookup(path)
20
+ return nil if path == nil || path.to_s.empty?
21
+ path = path.to_s
22
+ path_list = path.split('.')
23
+
24
+ o = self
25
+ path_list.each_with_index do |pe, idx|
26
+ o = o[pe] || o[pe.to_sym]
27
+ return nil if o == nil || !o.is_a?(Hash) && idx < path_list.length - 1
28
+ end
29
+ o
30
+ end
31
+ end
32
+
33
+
34
+ require 'appconf_version'
35
+ class AppConfig
36
+
37
+ class ConfigError < RuntimeError; end
38
+
39
+ CONFIG_BASE_NAME = 'config'
40
+
41
+ def self.setup(app_name, app_root_dir, deploy_conf_dir = nil)
42
+ raise ArgumentError, 'app name cannot be empty' if app_name.nil? or app_name.length == 0
43
+ raise ArgumentError, 'app root directory not specified' if app_root_dir.nil? or app_root_dir.length == 0
44
+ raise ArgumentError, 'app root directory does not exist or not a directory' unless File.directory?(app_root_dir)
45
+
46
+ @@app_raw_name = app_name
47
+ @@app_name = app_name.downcase
48
+ @@app_root_dir = File.expand_path(app_root_dir)
49
+
50
+ raise ArgumentError, 'deploy config directory does not exist or not a directory' if deploy_conf_dir && !File.directory?(deploy_conf_dir)
51
+ @@deploy_config_dir = deploy_conf_dir or ENV[@base_name.upcase + '_CONF_DIR']
52
+ raise ConfigError, 'config directory `%s\' specifed by environment does not exist or not a directory' %
53
+ @@deploy_config_dir if @@deploy_config_dir && !File.directory?(@@deploy_config_dir)
54
+ end
55
+ def self.app_name
56
+ @@app_raw_name
57
+ end
58
+ def self.get_config config_name = nil, config_dir = nil
59
+ raise ConfigError, ('please call %s::setup() before getting a config instance' % self.name) if @@app_name.nil?
60
+ self.new config_dir, config_name
61
+ end
62
+
63
+ def [] key
64
+ @deploy_platform_config.path_lookup(key) ||
65
+ @deploy_config.path_lookup(key) ||
66
+ @user_platform_config.path_lookup(key) ||
67
+ @user_config.path_lookup(key) ||
68
+ @global_platform_config.path_lookup(key) ||
69
+ @global_config.path_lookup(key) ||
70
+ @dist_platform_config.path_lookup(key) ||
71
+ @dist_config.path_lookup(key)
72
+ end
73
+
74
+ def reload
75
+ @deploy_platform_config = deploy_platform_config || {}
76
+ @deploy_config = deploy_config || {}
77
+
78
+ @user_platform_config = user_platform_config || {}
79
+ @user_config = user_config || {}
80
+
81
+ @global_platform_config = global_platform_config || {}
82
+ @global_config = global_config || {}
83
+
84
+ @dist_platform_config = dist_platform_config || {}
85
+ @dist_config = dist_config || {}
86
+ end
87
+
88
+ attr_reader :config_files, :config_file_name, :platform_config_file_name,
89
+ :dist_config_dir, :global_config_dir, :user_config_dir, :deploy_config_dir
90
+
91
+ private
92
+
93
+ def initialize(config_name = nil, config_dir = nil)
94
+ raise ArgumentError, 'specified config directory `%s\' does not exist'%config_dir if config_dir && !File.exist?(config_dir)
95
+
96
+ @base_name = config_name || @@app_name
97
+ @deploy_config_dir = config_dir || @@deploy_config_dir
98
+ @deploy_config_dir = nil if config_dir && config_dir.length == 0
99
+ @deploy_config_dir = File.expand_path(@deploy_config_dir) if @deploy_config_dir
100
+
101
+ @config_file_name = '%s.%s.yaml' % [@base_name, CONFIG_BASE_NAME]
102
+ @platform_config_file_name = '%s.%s.%s.yaml' % [@base_name, RUBY_PLATFORM, CONFIG_BASE_NAME]
103
+
104
+ @dist_config_dir = File.join @@app_root_dir, CONFIG_BASE_NAME
105
+ @global_config_dir = sys_config_dir
106
+ @user_config_dir = usr_config_dir
107
+
108
+ @config_files = []
109
+ reload
110
+ end
111
+
112
+ def dist_config
113
+ file_path = File.join @dist_config_dir, @config_file_name
114
+ if File.exist?(file_path)
115
+ @config_files << file_path
116
+ return load_yaml_file file_path
117
+ end
118
+ nil
119
+ end
120
+ def dist_platform_config
121
+ file_path = File.join @dist_config_dir, @platform_config_file_name
122
+ if File.exist?(file_path)
123
+ @config_files << file_path
124
+ return load_yaml_file file_path
125
+ end
126
+ nil
127
+ end
128
+ def global_config
129
+ file_path = File.join @global_config_dir, @config_file_name
130
+ if File.exist?(file_path)
131
+ @config_files << file_path
132
+ return load_yaml_file file_path
133
+ end
134
+ nil
135
+ end
136
+ def global_platform_config
137
+ file_path = File.join @global_config_dir, @platform_config_file_name
138
+ if File.exist?(file_path)
139
+ @config_files << file_path
140
+ return load_yaml_file file_path
141
+ end
142
+ nil
143
+ end
144
+ def user_config
145
+ file_path = File.join @user_config_dir, @config_file_name
146
+ if File.exist?(file_path)
147
+ @config_files << file_path
148
+ return load_yaml_file file_path
149
+ end
150
+ nil
151
+ end
152
+ def user_platform_config
153
+ file_path = File.join @user_config_dir, @platform_config_file_name
154
+ if File.exist?(file_path)
155
+ @config_files << file_path
156
+ return load_yaml_file file_path
157
+ end
158
+ nil
159
+ end
160
+ def deploy_config
161
+ return nil if @deploy_config_dir.nil?
162
+ file_path = File.join @deploy_config_dir, @config_file_name
163
+ if file_path && File.file?(file_path)
164
+ @config_files << file_path
165
+ return load_yaml_file(file_path)
166
+ end
167
+ nil
168
+ end
169
+ def deploy_platform_config
170
+ return nil if @deploy_config_dir.nil?
171
+ file_path = File.join @deploy_config_dir, @platform_config_file_name
172
+ if file_path && File.file?(file_path)
173
+ @config_files << file_path
174
+ return load_yaml_file(file_path)
175
+ end
176
+ nil
177
+ end
178
+
179
+ def load_yaml_file(file_path)
180
+ YAML.load(IO.read(file_path)) if File.file?(file_path)
181
+ end
182
+
183
+ def sys_config_dir
184
+ if ENV['OS'] == 'Windows_NT'
185
+ dir = File.join(ENV['ALLUSERSPROFILE'], 'Application Data', @@app_name)
186
+ else
187
+ dir = File.join('/etc', @@app_name)
188
+ end
189
+ return File.expand_path(dir)
190
+ end
191
+ def usr_config_dir
192
+ if ENV['OS'] == 'Windows_NT'
193
+ dir = File.join(ENV['USERPROFILE'], @@app_name)
194
+ else
195
+ dir = File.join(ENV['HOME'], '.' + @@app_name)
196
+ end
197
+ return File.expand_path(dir)
198
+ end
199
+ end
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ class AppConfig
4
+ VERSION = '0.0.1'
5
+ end
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- encoding: utf-8 -*-
3
+
4
+ require 'minitest/autorun'
5
+ require 'appconf' # use this with Rake
6
+ #require_relative '../lib/appconf' # use this without Rake
7
+
8
+ class AppConfigTest < MiniTest::Unit::TestCase
9
+ DEPLOY_VALUE = 'deployment value'
10
+ DEPLOY_PLATFORM_VALUE = 'deployment platform value'
11
+ USER_VALUE = 'user value'
12
+ USER_PLATFORM_VALUE = 'user platform value'
13
+ GLOBAL_VALUE = 'global value'
14
+ GLOBAL_PLATFORM_VALUE = 'global platform value'
15
+ DIST_VALUE = 'distribution value'
16
+ DIST_PLATFORM_VALUE = 'distribution platform value'
17
+
18
+ TEST_CONFIG_NAME = 'foo.bar'
19
+
20
+ # a new TestCase object created for every test method testing
21
+ AppConfig.setup 'Test', File.dirname(__FILE__), ENV['TMP']
22
+ @@config = AppConfig.get_config
23
+
24
+ def self.clean
25
+ #puts 'AppConfigTest::clean called.'
26
+ @@config.config_files.each { |f| File.delete f if File.exist? f }
27
+ begin
28
+ Dir.rmdir @@config.dist_config_dir if File.exist? @@config.dist_config_dir
29
+ Dir.rmdir @@config.user_config_dir if File.exist? @@config.user_config_dir
30
+ Dir.rmdir @@config.global_config_dir if File.exist? @@config.global_config_dir
31
+ rescue
32
+ $stderr.puts $!
33
+ end
34
+ end
35
+
36
+ self.clean
37
+
38
+ def self.test_order
39
+ :alpha
40
+ end
41
+
42
+ def test_h_deploy_platform_value
43
+ create_deploy_platform_config_file
44
+ @@config.reload
45
+ assert_equal DEPLOY_PLATFORM_VALUE, query_config_value
46
+ end
47
+ def test_g_deploy_value
48
+ create_deploy_config_file
49
+ @@config.reload
50
+ assert_equal DEPLOY_VALUE, query_config_value
51
+ end
52
+ def test_f_user_platform_value
53
+ create_user_platform_config_file
54
+ @@config.reload
55
+ assert_equal USER_PLATFORM_VALUE, query_config_value
56
+ end
57
+ def test_e_user_value
58
+ create_user_config_file
59
+ @@config.reload
60
+ assert_equal USER_VALUE, query_config_value
61
+ end
62
+ def test_d_global_platform_value
63
+ create_global_platform_config_file
64
+ @@config.reload
65
+ assert_equal GLOBAL_PLATFORM_VALUE, query_config_value
66
+ end
67
+ def test_c_global_value
68
+ create_global_config_file
69
+ @@config.reload
70
+ assert_equal GLOBAL_VALUE, query_config_value
71
+ end
72
+ def test_b_dist_platform_value
73
+ create_dist_platform_config_file
74
+ @@config.reload
75
+ assert_equal DIST_PLATFORM_VALUE, query_config_value
76
+ end
77
+ def test_a_dist_value
78
+ create_dist_config_file
79
+ @@config.reload
80
+ assert_equal DIST_VALUE, query_config_value
81
+ end
82
+ def test_i_non_exist_value
83
+ assert_equal nil, @@config['non.exist.config.item']
84
+ end
85
+
86
+ def test_z_clean
87
+ self.class.clean
88
+ end
89
+
90
+ private
91
+
92
+ def query_config_value
93
+ @@config[TEST_CONFIG_NAME]
94
+ end
95
+
96
+ def create_yaml_with_value(path, v, more = nil)
97
+ c = { :foo => { :bar => v } }
98
+ c.merge! more if more and more.is_a?(Hash)
99
+ dir = File.dirname(path)
100
+ Dir.mkdir dir unless File.exist?(dir)
101
+ File.open(path, 'w') { |f| f.write c.to_yaml }
102
+ puts 'YAML created: %s' % path
103
+ end
104
+ def create_deploy_config_file
105
+ path = File.join @@config.deploy_config_dir, @@config.config_file_name
106
+ create_yaml_with_value path, DEPLOY_VALUE
107
+ end
108
+ def create_deploy_platform_config_file
109
+ path = File.join @@config.deploy_config_dir, @@config.platform_config_file_name
110
+ create_yaml_with_value path, DEPLOY_PLATFORM_VALUE
111
+ end
112
+ def create_user_config_file
113
+ path = File.join @@config.user_config_dir, @@config.config_file_name
114
+ create_yaml_with_value path, USER_VALUE
115
+ end
116
+ def create_user_platform_config_file
117
+ path = File.join @@config.user_config_dir, @@config.platform_config_file_name
118
+ create_yaml_with_value path, USER_PLATFORM_VALUE
119
+ end
120
+ def create_global_config_file
121
+ path = File.join @@config.global_config_dir, @@config.config_file_name
122
+ create_yaml_with_value path, GLOBAL_VALUE
123
+ end
124
+ def create_global_platform_config_file
125
+ path = File.join @@config.global_config_dir, @@config.platform_config_file_name
126
+ create_yaml_with_value path, GLOBAL_PLATFORM_VALUE
127
+ end
128
+ def create_dist_config_file
129
+ path = File.join @@config.dist_config_dir, @@config.config_file_name
130
+ create_yaml_with_value path, DIST_VALUE
131
+ end
132
+ def create_dist_platform_config_file
133
+ path = File.join @@config.dist_config_dir, @@config.platform_config_file_name
134
+ create_yaml_with_value path, DIST_PLATFORM_VALUE
135
+ end
136
+
137
+ end
138
+
139
+ # this line does not work
140
+ # MiniTest::Unit.after_tests { AppConfigTest.clean }
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: appconf
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Arloan Bone
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2016-04-07 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Application configuration file loader & querier
15
+ email: arloan@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/appconf.rb
21
+ - lib/appconf_version.rb
22
+ - test/test_appconf.rb
23
+ - Rakefile
24
+ - README.md
25
+ homepage: https://github.com/arloan/appconf
26
+ licenses:
27
+ - MIT
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubyforge_project:
46
+ rubygems_version: 1.8.24
47
+ signing_key:
48
+ specification_version: 3
49
+ summary: AppConf
50
+ test_files: []