config_plus 0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 20890e554dc232e7ec1a72414f746b15ac7a3449
4
+ data.tar.gz: efacb601ce0541c0e2141e6846bede15ab2cb437
5
+ SHA512:
6
+ metadata.gz: fb3a6f9ec6260f0ed6299053898d1638219d9a1146c9e117f5c28b8708d74abc1853fd4395ff1b531eeaae23dea1b86ba9dd7391e7503018ec9152047c9b52f4
7
+ data.tar.gz: 48713b64f82b3b38a8b3c486af64fa34e9ef38732ebebd091ef591384d1d9392ae6bff0cc8f737ff7e51b3de2cecbded7cd11b545860ab92582f50af05d1c650
data/README.ja.md ADDED
@@ -0,0 +1,137 @@
1
+ ConfigPlus
2
+ ==================================================
3
+
4
+ `ConfigPlus` は YAML ファイルから設定情報を読み込むためのライブラリです。
5
+ 読み込みたい YAML ファイルのパスを指定すると、`ConfigPlus.root` から設定情報にアクセスできるようになります。
6
+
7
+ 基本的な使用例
8
+ --------------------------------------------------
9
+ 例として、次のような YAML ファイルがあるとすると、
10
+
11
+ ```yaml
12
+ foo:
13
+ baa:
14
+ baz:
15
+ spam: 123
16
+ ham: abc
17
+ ```
18
+
19
+ 次のようにアクセスできます。
20
+
21
+ ```ruby
22
+ ConfigPlus.generate(from: '/path/to/configuration/file.yml')
23
+ ConfigPlus.root[:foo][:baa][:baz]
24
+ #=> {"spam"=>123, "ham"=>"abc"}
25
+
26
+ ConfigPlus.root['foo']['baa']['baz']
27
+ #=> {"spam"=>123, "ham"=>"abc"}
28
+
29
+ ConfigPlus.root.foo.baa.baz
30
+ #=> {"spam"=>123, "ham"=>"abc"}
31
+
32
+ ConfigPlus.root.get('foo.baa.baz')
33
+ #=> {"spam"=>123, "ham"=>"abc"}
34
+ ```
35
+
36
+ メソッド風のアクセスには多少の制限があります。
37
+ キーがメソッド名として有効である必要があるのはもちろんのこと、private
38
+ メソッドも含めて既存のメソッド名と重複した場合は、既存のメソッドの方が有効になります。
39
+
40
+ `ConfigPlus.root` は `Hash` を拡張したオブジェクトなので、
41
+ おおよそ `Hash` のメソッドを上書きできない(正確には `ConfigPlus::Node`
42
+ のメソッドを上書きできない)、ということになります。
43
+
44
+ 自動マッピング
45
+ --------------------------------------------------
46
+ YAML の構造と同じパスを持つクラス(上記の場合であれば `Foo::Baa::Baz` というクラス)がある場合、
47
+ 次のようにして設定情報にアクセスすることもできます。
48
+
49
+ ```ruby
50
+ module Foo
51
+ module Baa
52
+ class Baz
53
+ include ConfigPlus
54
+ end
55
+ end
56
+ end
57
+
58
+ Foo::Baa::Baz.config
59
+ #=> {"spam"=>123, "ham"=>"abc"}
60
+
61
+ Foo::Baa::Baz.config.spam
62
+ #=> 123
63
+
64
+ baz = Foo::Baa::Baz.new
65
+ baz.config.ham
66
+ #=> "abc"
67
+ ```
68
+
69
+ `ConfigPlus` を `include` することで、クラスメソッドとインスタンスメソッドに
70
+ `config` というメソッドが追加されます(このメソッド名は設定で変更できます)。
71
+ そこから、読み込んだ設定情報にアクセスできるようになります。
72
+
73
+ 上書きマージ
74
+ --------------------------------------------------
75
+ `ConfigPlus` に読み込ませるパスには、ディレクトリを指定することもできます。
76
+ ディレクトリを指定した場合は、サブディレクトリも含めてファイルが探索され、
77
+ その中から拡張子がマッチしたものが全て読み込まれます。
78
+ 読み込まれた YAML は、最終的に一つにマージされた Hash となります。
79
+
80
+ 設定ファイルはファイル名でソートされた上で読み込まれ、マージされます。
81
+ このため、同じ設定情報があればソート順の大きい方で上書きされます。
82
+
83
+ 例として、次のようなファイルが同じディレクトリに配置されていたとすると、
84
+
85
+ ```yml
86
+ # sample-00.yml
87
+ sample:
88
+ setting_a:
89
+ spam: spam-00
90
+ ham: ham-00
91
+ egg: egg-00
92
+ ```
93
+
94
+ ```yml
95
+ # sample-01.yml
96
+ sample:
97
+ setting_a:
98
+ ham: ham-01
99
+ ```
100
+
101
+ `sample-00.yml` がベースとなり、そこに `sample-01.yml` が上書きされて読み込まれます。
102
+
103
+ ```ruby
104
+ Sample::SettingA.config
105
+ #=> {"spam"=>"spam-00", "ham"=>"ham-01", "egg"=>"egg-00"}
106
+ ```
107
+
108
+ その他
109
+ --------------------------------------------------
110
+ `ConfigPlus` の動作は設定で変更できます。
111
+ 設定ファイルへのパス以外の設定がある場合、次のようにブロックを使って記述します。
112
+
113
+ ```ruby
114
+ ConfigPlus.configure do |conf|
115
+ conf.root_dir = Rails.root
116
+ conf.source = 'config/config_plus'
117
+ conf.config_method = :setting
118
+ conf.extension = '00.yml'
119
+ conf.namespace = Rails.env
120
+ end
121
+ ```
122
+
123
+ `configure` メソッドは `generate` とほぼ同じ処理をするので、上記のように書けば改めて `generate` する必要はありません。
124
+
125
+ 主な設定を簡単に説明すると、次のようになります。
126
+
127
+ | 設定 | 概要説明 | 初期値 |
128
+ | --------------- | -------------------------------------------------------------------- | ----------------- |
129
+ | `config_method` | `include ConfigPlus` で自動マッピングした際の設定読み込みメソッド名 | `config` |
130
+ | `extension` | 読み込むファイルの拡張子。`source` がディレクトリの場合に有効 | `['yml', 'yaml']` |
131
+ | `namespace` | 使用するネームスペース | |
132
+ | `root_dir` | `source` で指定するパスの親ディレクトリのパス | |
133
+ | `source` | 設定ファイル、またはそれが格納されているディレクトリのパス | |
134
+
135
+ ライセンス
136
+ --------------------------------------------------
137
+ `ConfigPlus` は MIT ライセンスです。
data/README.md ADDED
@@ -0,0 +1,146 @@
1
+ ConfigPlus
2
+ ============================================================
3
+
4
+ An easy-to-use, powerful configuration module using YAML files
5
+ for Ruby.
6
+
7
+
8
+ Simple Usage
9
+ ------------------------------------------------------------
10
+ Add a configuration file in YAML format in your project:
11
+
12
+ ```yml
13
+ foo:
14
+ baa:
15
+ baz:
16
+ spam: 123
17
+ ham: abc
18
+ ```
19
+
20
+ You can access the configuration values with `ConfigPlus.root`:
21
+
22
+ ```ruby
23
+ ConfigPlus.generate(from: '/path/to/configuration/file.yml')
24
+ ConfigPlus.root.foo.baa.baz.spam
25
+ #=> 123
26
+
27
+ ConfigPlus.root['foo']['baa']['baz']['spam']
28
+ #=> 123
29
+
30
+ ConfigPlus.root[:foo][:baa][:baz][:spam]
31
+ #=> 123
32
+
33
+ ConfigPlus.root.get('foo.baa.baz.spam')
34
+ #=> 123
35
+ ```
36
+
37
+ `ConfigPlus` recurses a file tree looking for configuration files
38
+ when you specify a directory path at `ConfigPlus.generate from:`.
39
+
40
+ ```ruby
41
+ ConfigPlus.generate(from: '/path/to/configuration/directory')
42
+ ```
43
+
44
+ And you can specify some pathes using an array.
45
+
46
+ ```ruby
47
+ ConfigPlus.generate(from: ['/path/to/directory1', '/path/to/file1.yml'])
48
+ ```
49
+
50
+
51
+ Auto Mapping
52
+ ------------------------------------------------------------
53
+ When data structure of loaded YAML and class structure of your
54
+ Ruby project have the same hierarchy, accessing the configuration
55
+ can be more simple:
56
+
57
+ ```yml
58
+ fizz:
59
+ buzz:
60
+ spam: bacon
61
+ ham: sausage
62
+ ```
63
+
64
+ ```ruby
65
+ ConfigPlus.generate(from: '/path/to/configuration/file.yml')
66
+
67
+ module Fizz
68
+ class Buzz
69
+ include ConfigPlus
70
+ end
71
+ end
72
+
73
+ Fizz::Buzz.config.ham
74
+ #=> "sausage"
75
+
76
+ buzz = Fizz:Buzz.new
77
+ buzz.config.spam
78
+ #=> "bacon"
79
+ ```
80
+
81
+
82
+ Overwrite Merge
83
+ ------------------------------------------------------------
84
+ `ConfigPlus` loads the specified configuration files in file
85
+ name order and merge all of configuration into a single hash.
86
+
87
+ ```yml
88
+ # sample-00.yml
89
+
90
+ sample:
91
+ setting_a:
92
+ spam: bacon
93
+ ham: sausage
94
+ egg: baked beans
95
+ ```
96
+
97
+ ```yml
98
+ # sample-01.yml
99
+
100
+ sample:
101
+ setting_a:
102
+ ham: spam
103
+ ```
104
+
105
+ ```ruby
106
+ Sample::SettingA.config
107
+ #=> {"spam"=>"bacon", "ham"=>"spam", "egg"=>"baked beans"}
108
+ ```
109
+
110
+
111
+ Others
112
+ ------------------------------------------------------------
113
+ Settings of `ConfigPlus` can be changed by the following way:
114
+
115
+ ```ruby
116
+ ConfigPlus.configure do |conf|
117
+ conf.root_dir = Rails.root
118
+ conf.source = 'config/config_plus'
119
+ conf.config_method = :setting
120
+ conf.extension = [:yml, :yaml]
121
+ conf.namespace = Rails.env
122
+ end
123
+ ```
124
+
125
+ `configure` method works in a similar way as `generate` method.
126
+ Properties you can set are following:
127
+
128
+ * `config_method`
129
+ * a method name to access configuration using in a class
130
+ which does `include ConfigPlus`
131
+ * `extension`
132
+ * extensions of configuration files which you allow to be
133
+ loaded when you specify a directory path as `source`
134
+ * `namespace`
135
+ * load configuration only from a tree of which first
136
+ hierarchy key name is matched with the specified name
137
+ * `root_dir`
138
+ * used as a parent directory path when you specify a
139
+ relative path as `source`
140
+ * `source`
141
+ * a file path or a directory path or an array of them
142
+
143
+
144
+ License
145
+ ------------------------------------------------------------
146
+ MIT License
@@ -0,0 +1,8 @@
1
+ require 'config_plus/base'
2
+ require 'config_plus/config'
3
+ require 'config_plus/default_loader_logic'
4
+ require 'config_plus/helper'
5
+ require 'config_plus/loader'
6
+ require 'config_plus/merger'
7
+ require 'config_plus/node'
8
+ require 'config_plus/version'
@@ -0,0 +1,83 @@
1
+ module ConfigPlus
2
+ class << self
3
+ attr_reader :root
4
+
5
+ # Sets up configuration of ++ConfigPlus++ and loads data
6
+ #
7
+ # When a YAML file path is specified with
8
+ # ++source++ (or ++root_dir++) setting as below,
9
+ # configuration data would be loaded from the file.
10
+ #
11
+ # ConfigPlus.configure do |conf|
12
+ # conf.source = '/path/to/yaml/file.yml'
13
+ # end
14
+ #
15
+ # When a directory path is specified, configuration
16
+ # would be loaded from all YAML files under the
17
+ # specified directory.
18
+ #
19
+ def configure
20
+ yield config if block_given?
21
+ load
22
+ end
23
+
24
+ # Sets up configuration of ++ConfigPlus++ and loads data
25
+ #
26
+ # You can describe the following code, when it needs
27
+ # only a single file for a resource of ++ConfigPlus++.
28
+ #
29
+ # ConfigPlus.generate(from: '/path/to/yaml/file.yml')
30
+ #
31
+ def generate(from: nil, **properties)
32
+ config.source = from if from
33
+ properties.each do |k, v|
34
+ attr = "#{k}="
35
+ config.public_send(attr, v) if config.respond_to? attr
36
+ end
37
+ load
38
+ end
39
+
40
+ protected
41
+
42
+ def config
43
+ @config ||= ::ConfigPlus::Config.new
44
+ end
45
+
46
+ private
47
+
48
+ # loads a configuration data as a hash object
49
+ # from files specified with ++source++ or
50
+ # ++root_dir++ settings.
51
+ #
52
+ def load
53
+ hash = config.loader.load
54
+ @root = ::ConfigPlus::Node.new(hash)
55
+ end
56
+ end
57
+
58
+ def self.included(base)
59
+ method_name = self.config.config_method
60
+ return unless method_name
61
+ variable_name = "@#{method_name}"
62
+ helper = ::ConfigPlus::Helper
63
+ own = helper.config_for(base, ::ConfigPlus.root)
64
+
65
+ inheritance = base.ancestors.select {|klass|
66
+ klass != base and
67
+ klass != ConfigPlus and
68
+ klass.ancestors.include?(ConfigPlus)
69
+ }.reverse.each_with_object({}) {|klass, hsh|
70
+ h = klass.public_send(method_name)
71
+ h = helper.config_for(klass, ::ConfigPlus.root) unless
72
+ h or h.is_a?(Hash)
73
+ hsh.merge!(h)
74
+ }
75
+
76
+ [base, base.singleton_class].each do |obj|
77
+ obj.instance_eval do
78
+ config = inheritance ? inheritance.merge(own || {}) : own
79
+ define_method method_name, -> { config }
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,33 @@
1
+ module ConfigPlus
2
+ class Config
3
+ attr_accessor :config_method,
4
+ :extension,
5
+ :namespace,
6
+ :root_dir,
7
+ :source
8
+ attr_reader :version
9
+ attr_writer :loader_logic
10
+
11
+ def initialize
12
+ @version = VERSION
13
+ @config_method = :config
14
+ @extension = nil
15
+ @loader_logic = :default
16
+ @namespace = nil
17
+ @root_dir = nil
18
+ @source = nil
19
+ end
20
+
21
+ def loader
22
+ Loader.new(self)
23
+ end
24
+
25
+ def loader_logic
26
+ name = @loader_logic.to_s.capitalize
27
+ name = "#{name}LoaderLogic"
28
+ raise "Unknown loader logic named `#{name}'" unless
29
+ ConfigPlus::const_defined?(name)
30
+ ConfigPlus::const_get(name)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,31 @@
1
+ require 'yaml'
2
+
3
+ module ConfigPlus
4
+ class DefaultLoaderLogic
5
+ def initialize(config)
6
+ @config = config
7
+ end
8
+
9
+ def extension
10
+ @config.extension || [:yml, :yaml]
11
+ end
12
+
13
+ def load_from(path)
14
+ return load_file(path) if File.file?(path)
15
+ load_dir(path)
16
+ end
17
+
18
+ def load_file(filepath)
19
+ content = open(filepath).read
20
+ YAML.load(content).to_hash
21
+ end
22
+
23
+ def load_dir(dirpath)
24
+ ext = Array(extension).join(',')
25
+ path = File.join(dirpath, '**', "*.{#{ext}}")
26
+ Dir.glob(path).sort.inject({}) {|h, filepath|
27
+ Merger.merge(h, load_file(filepath))
28
+ }
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,39 @@
1
+ module ConfigPlus
2
+ module Helper
3
+ class << self
4
+ def config_for(object, config)
5
+ configs = matched_configs(object, config)
6
+ return configs.first if configs.size <= 1
7
+
8
+ if configs.all? {|c| c.is_a? Hash }
9
+ configs.inject(::ConfigPlus::Node.new) {|h, conf|
10
+ ::ConfigPlus::Merger.merge(h, conf)
11
+ }
12
+ else
13
+ configs.inject([]) {|a, conf| a << conf }
14
+ end
15
+ end
16
+
17
+ def underscore(name)
18
+ name.gsub(/::/, '.')
19
+ .gsub(/((\A|\b)([A-Z]+))|([A-Z]+)/) do
20
+ next $3.downcase if $3
21
+ "_#{$4.downcase}"
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def matched_configs(object, node)
28
+ return [] unless node
29
+ mod = object.is_a?(Module) ? object : object.class
30
+ path = underscore(mod.name)
31
+
32
+ [node[mod.name],
33
+ node[path],
34
+ node.get(path),
35
+ ].uniq.compact
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,37 @@
1
+ module ConfigPlus
2
+ class Loader
3
+ def initialize(config)
4
+ @config = config
5
+ end
6
+
7
+ def load
8
+ paths = source_paths
9
+ raise "No specified `source'" if paths.empty?
10
+
11
+ paths.each.inject({}) do |h, path|
12
+ hsh = loader_logic.load_from(path)
13
+ hsh = hsh[@config.namespace.to_s] if @config.namespace
14
+ Merger.merge(h, hsh)
15
+ end
16
+ end
17
+
18
+ protected
19
+
20
+ def loader_logic
21
+ @loader_logic ||= @config.loader_logic.new(@config)
22
+ end
23
+
24
+ def source_paths
25
+ Array(@config.source).map {|s|
26
+ source_path(s)
27
+ }.reverse.uniq.compact.reverse
28
+ end
29
+
30
+ def source_path(filepath)
31
+ return filepath unless @config.root_dir
32
+ return @config.root_dir unless filepath
33
+ return filepath if filepath.start_with?('/')
34
+ File.join(@config.root_dir, filepath)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,23 @@
1
+ module ConfigPlus
2
+ module Merger
3
+ MERGER = ->(key, h1, h2) do
4
+ if h1.is_a?(Hash) and h2.is_a?(Hash)
5
+ h1.merge(h2, &MERGER)
6
+ else
7
+ h2
8
+ end
9
+ end
10
+
11
+ def self.merge(collection1, collection2)
12
+ return collection2 unless collection1
13
+ return collection1 unless collection2
14
+
15
+ if collection1.is_a?(Array) and
16
+ collection2.is_a?(Array)
17
+ collection1.concat(collection2)
18
+ else
19
+ collection1.merge(collection2, &MERGER)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,51 @@
1
+ module ConfigPlus
2
+ class Node < Hash
3
+ def initialize(hash = nil)
4
+ node = super()
5
+ node.merge!(hash) if hash
6
+ end
7
+
8
+ def [](key)
9
+ value = self.fetch(key.to_s, nil)
10
+ value = self.fetch(key.to_i, nil) if
11
+ value.nil? and key.to_s =~ /\A\d+\z/
12
+ return value unless value.is_a?(Hash)
13
+ return value if value.is_a?(self.class)
14
+
15
+ self.store(key.to_s, self.class.new(value))
16
+ end
17
+
18
+ def get(path)
19
+ key, rest = path.split('.', 2)
20
+ return self[key] unless rest
21
+ return nil unless self[key]
22
+ self[key].get(rest)
23
+ end
24
+
25
+ def merge!(hash)
26
+ result = super
27
+ hash.keys.each {|k| define_accessor(k) }
28
+ result
29
+ end
30
+
31
+ def merge(hash)
32
+ result = super
33
+ result.instance_eval {
34
+ hash.keys.each {|k| define_accessor(k) }
35
+ }
36
+ result
37
+ end
38
+
39
+ private
40
+
41
+ def define_accessor(method_name)
42
+ return unless method_name.is_a?(String) or
43
+ method_name.is_a?(Symbol)
44
+ return if respond_to?(method_name) or
45
+ private_methods.include?(method_name.to_sym)
46
+ singleton_class.class_eval do
47
+ define_method method_name, -> { self[method_name] }
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,3 @@
1
+ module ConfigPlus
2
+ VERSION = '0.0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: config_plus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - m4oda
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-04-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.4'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.4'
27
+ description: ConfigPlus is an easy-to-use configuration module that uses YAML files
28
+ and has powerful features such as auto mapping.
29
+ email: e5ww2sze@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - README.ja.md
35
+ - README.md
36
+ - lib/config_plus.rb
37
+ - lib/config_plus/base.rb
38
+ - lib/config_plus/config.rb
39
+ - lib/config_plus/default_loader_logic.rb
40
+ - lib/config_plus/helper.rb
41
+ - lib/config_plus/loader.rb
42
+ - lib/config_plus/merger.rb
43
+ - lib/config_plus/node.rb
44
+ - lib/config_plus/version.rb
45
+ homepage: https://github.com/m4oda/config_plus
46
+ licenses:
47
+ - MIT
48
+ metadata: {}
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project:
65
+ rubygems_version: 2.4.5
66
+ signing_key:
67
+ specification_version: 4
68
+ summary: An easy-to-use, powerful configuration module using YAML files
69
+ test_files: []