configurates 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +13 -0
- data/LICENSE +9 -0
- data/Rakefile +7 -0
- data/lib/configurates.rb +101 -0
- data/lib/configurates/provider.rb +89 -0
- data/lib/configurates/provider/environment.rb +34 -0
- data/lib/configurates/provider/yaml.rb +42 -0
- data/lib/configurates/storage.rb +183 -0
- data/readme.md +98 -0
- data/spec/config_spec.rb +32 -0
- data/spec/customizable_configuration/provider/environment_spec.rb +20 -0
- data/spec/customizable_configuration/provider/yaml_spec.rb +20 -0
- data/spec/customizable_configuration/provider_spec.rb +34 -0
- data/spec/customizable_configuration/storage_spec.rb +69 -0
- data/spec/spec_helper.rb +6 -0
- metadata +66 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0e9942377c030d89736ff37299d6ba12de638b48
|
4
|
+
data.tar.gz: 6a9f55676dbe4c957633bd664dcaea9df6bdcd3e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: dcb4745cdfff415407998781a4bcf762a0914f5576cd47057b80a66df178ba7a78e83476c42f6737bdea6b5ad0403e81461f97a633f2a6992358319ae2c34aba
|
7
|
+
data.tar.gz: 0b2ee8cab588f48fc993e17e424c41542a963c9e181cb137b49579489e7fdd398ca7384dceb6249444b26396987b485101f93b0626a7543df0eba89b934e1e4c
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) <2016> Medvedu
|
4
|
+
|
5
|
+
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:
|
6
|
+
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
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.
|
data/Rakefile
ADDED
data/lib/configurates.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
require File.join(__dir__, 'configurates', 'provider')
|
6
|
+
require File.join(__dir__, 'configurates', 'provider', 'yaml')
|
7
|
+
require File.join(__dir__, 'configurates', 'provider', 'environment')
|
8
|
+
require File.join(__dir__, 'configurates', 'storage')
|
9
|
+
|
10
|
+
#
|
11
|
+
# Settings::Config is a module that provides agile way to work with
|
12
|
+
# configuration, which can be useful for object initialization with default
|
13
|
+
# settings. See details below:
|
14
|
+
#
|
15
|
+
# class MyClass
|
16
|
+
#
|
17
|
+
# def initialize
|
18
|
+
# config = Settings::Config.create do
|
19
|
+
# merge_storage( Settings::Yaml.load('path/to/my/yaml/file'),
|
20
|
+
# root: 'myclass/default' )
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# if config.download?
|
24
|
+
# @power = config.power
|
25
|
+
# @value = config.actual.value
|
26
|
+
# else
|
27
|
+
# print config['WARNING_MESSAGE']
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# This code demonstrates next concepts:
|
33
|
+
#
|
34
|
+
# First. You can choose what config part should be loaded. It can be useful for
|
35
|
+
# small projects where you do not want to create more than one config file. It
|
36
|
+
# means even if whole config placed in one place it is possible to load small
|
37
|
+
# data/file part.
|
38
|
+
#
|
39
|
+
# Second. You can select an object from where configs can be loaded. By default
|
40
|
+
# it can be Settings::Yaml or Settings::ENV provider
|
41
|
+
#
|
42
|
+
# Third. Chains are allowed.
|
43
|
+
#
|
44
|
+
#
|
45
|
+
module Settings
|
46
|
+
#
|
47
|
+
#
|
48
|
+
#
|
49
|
+
class Config
|
50
|
+
attr_reader :root
|
51
|
+
|
52
|
+
include Provider
|
53
|
+
|
54
|
+
#
|
55
|
+
# Currently provider(s) can be registered only when we creating new instance
|
56
|
+
# based on Settings:Config class. It means self.create should be a higher
|
57
|
+
# function.
|
58
|
+
#
|
59
|
+
def self.create(&callback)
|
60
|
+
msg = 'Provider not being chosen'
|
61
|
+
raise NoBlockGiven, msg unless block_given?
|
62
|
+
new(&callback)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
#
|
68
|
+
# Run &callback code in object instance.
|
69
|
+
#
|
70
|
+
def initialize(&callback)
|
71
|
+
instance_eval(&callback)
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# Provide message redirect in the @root (storage) if it's possible.
|
76
|
+
#
|
77
|
+
def method_missing(msg, *args, &blk)
|
78
|
+
message_for_root?(msg) ? @root.send(msg, *args, &blk) : super
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# And extend respond_to with storate's responses
|
83
|
+
#
|
84
|
+
def respond_to?(msg, include_private = false)
|
85
|
+
super || message_for_root?(msg) ? true : false
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
#
|
90
|
+
#
|
91
|
+
def message_for_root?(msg)
|
92
|
+
@root.send(:respond_to?, msg) ? true : false
|
93
|
+
end
|
94
|
+
|
95
|
+
#
|
96
|
+
# This is supposed to be raised when we trying to create instance for
|
97
|
+
# Settings::Config class without &codeblock
|
98
|
+
#
|
99
|
+
class NoBlockGiven < StandardError; end
|
100
|
+
end # class Settings::Config
|
101
|
+
end # module Settings
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Settings
|
4
|
+
#
|
5
|
+
# Module Provider is an inclusion for Settings::Config class.
|
6
|
+
#
|
7
|
+
# This module used for provider validations and data normalization.
|
8
|
+
#
|
9
|
+
# Details:
|
10
|
+
#
|
11
|
+
# Any provider (f.e. Settings::Yaml should respond to :data and :load
|
12
|
+
# methods, otherwise an exception will be raised.
|
13
|
+
#
|
14
|
+
# Any provider after provider.data method call should return non-empty hash,
|
15
|
+
# otherwise an exception will be raised.
|
16
|
+
#
|
17
|
+
# Provider should present non-empty sub-hash if optional parameter "#{root}"
|
18
|
+
# was used. For example for root: 'data/day1', original hash needs to include
|
19
|
+
# subhash like:
|
20
|
+
#
|
21
|
+
# hash = {…, …, data: {…, day1: { …, … }, … }, … }
|
22
|
+
#
|
23
|
+
module Provider
|
24
|
+
private
|
25
|
+
|
26
|
+
#
|
27
|
+
# Can be called only when Settings::Config class is initializing.
|
28
|
+
#
|
29
|
+
def merge_storage(provider, root: nil)
|
30
|
+
msg = "Provider #{provider} does not respond on :data or :load messages"
|
31
|
+
raise ProviderNotRespond, msg unless provider.class.respond_to?(:load) &&
|
32
|
+
provider.respond_to?(:data)
|
33
|
+
|
34
|
+
hsh = validate_and_extract_hash_from(provider)
|
35
|
+
extract_sub_hash_from(hsh, root)
|
36
|
+
defined?(@root) ? @root.merge(hsh) : @root = Storage.create(hsh)
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# mutate hash unless root parameter is nil. Raise an exception if
|
41
|
+
# mutation result is not a valid hash.
|
42
|
+
#
|
43
|
+
def extract_sub_hash_from(hsh, root)
|
44
|
+
return hsh if root.nil?
|
45
|
+
tmp = hsh
|
46
|
+
root.split('/').each do |path|
|
47
|
+
tmp = tmp[path.to_sym]
|
48
|
+
msg = "No sub hash with symbol name '#{path}' for #{hsh} was found!"
|
49
|
+
raise RootNotExist, msg unless hash_valid?(tmp)
|
50
|
+
end
|
51
|
+
hsh.replace tmp
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# encapsulate & extract & validate hash from provider
|
56
|
+
#
|
57
|
+
def validate_and_extract_hash_from(provider)
|
58
|
+
hsh = provider.data.dup
|
59
|
+
msg = "#{hsh} is not valid or empty Hash!"
|
60
|
+
raise HashNotValid, msg unless hash_valid?(hsh)
|
61
|
+
hsh
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# valid hash is a non-empty hash object
|
66
|
+
#
|
67
|
+
def hash_valid?(hsh)
|
68
|
+
hsh.is_a?(Hash) && !hsh.empty? ? true : false
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
# This is supposed to be raised when provider not respond to :data
|
73
|
+
# or :load messages
|
74
|
+
#
|
75
|
+
class ProviderNotRespond < StandardError; end
|
76
|
+
|
77
|
+
#
|
78
|
+
# This is supposed to be raised when hash is not really a Hash or just
|
79
|
+
# when it is empty.
|
80
|
+
#
|
81
|
+
class HashNotValid < StandardError; end
|
82
|
+
|
83
|
+
#
|
84
|
+
# This is supposed to be raised when we cant extract a valid sub-hash
|
85
|
+
# from hash. In other words Root for hash not exist.
|
86
|
+
#
|
87
|
+
class RootNotExist < StandardError; end
|
88
|
+
end # module Settings::Provider
|
89
|
+
end # module Settings
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Settings
|
4
|
+
#
|
5
|
+
#
|
6
|
+
#
|
7
|
+
class ENV
|
8
|
+
#
|
9
|
+
#
|
10
|
+
#
|
11
|
+
def self.load(hsh)
|
12
|
+
new(hsh)
|
13
|
+
end
|
14
|
+
|
15
|
+
#
|
16
|
+
#
|
17
|
+
#
|
18
|
+
attr_reader :data
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def initialize(hsh)
|
23
|
+
msg = "object #{hsh} should be an instance of Hash!"
|
24
|
+
raise HashNotValid, msg unless Hash === hsh
|
25
|
+
@data = hsh.dup
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# This is supposed to be raised when self.load got a wrong object which
|
30
|
+
# can't response as a Hash.
|
31
|
+
#
|
32
|
+
class HashNotValid < StandardError; end
|
33
|
+
end # class Settings::YAML
|
34
|
+
end # module Settings
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Settings
|
4
|
+
#
|
5
|
+
#
|
6
|
+
#
|
7
|
+
class Yaml
|
8
|
+
#
|
9
|
+
#
|
10
|
+
#
|
11
|
+
def self.load(path = nil)
|
12
|
+
path ||= '../configs/default.yml'
|
13
|
+
new(path)
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
#
|
18
|
+
#
|
19
|
+
attr_reader :data
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def initialize(path)
|
24
|
+
@path = File.absolute_path(path)
|
25
|
+
begin
|
26
|
+
@data = YAML.load_file(@path)
|
27
|
+
rescue Errno::ENOENT
|
28
|
+
msg = "file with path: #{@path} not found!"
|
29
|
+
raise FileNotFound, msg
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# file path
|
34
|
+
attr_reader :path
|
35
|
+
|
36
|
+
#
|
37
|
+
# This is supposed to be raised when file transerred to the self.load
|
38
|
+
# method wasn't found.
|
39
|
+
#
|
40
|
+
class FileNotFound < StandardError; end
|
41
|
+
end # class Settings::YAML
|
42
|
+
end # module Settings
|
@@ -0,0 +1,183 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Settings
|
4
|
+
#
|
5
|
+
#
|
6
|
+
#
|
7
|
+
class Storage
|
8
|
+
#
|
9
|
+
# Create new storage.
|
10
|
+
#
|
11
|
+
# Get hash and try to register each key as method or even sub-storage (it
|
12
|
+
# needs for nested hashes). Some keys are not allowed for method
|
13
|
+
# registration (f.e. when key = :42). All limitation about Ruby methods
|
14
|
+
# naming convention. You can get access to method with any name throw []
|
15
|
+
# thought.
|
16
|
+
#
|
17
|
+
# WARNING! Storage is case-sensitive! It means that keys :config and :Config
|
18
|
+
# are two different keys!
|
19
|
+
#
|
20
|
+
# Sample:
|
21
|
+
#
|
22
|
+
#
|
23
|
+
# hsh = { cinema: 'tiger',
|
24
|
+
# rate: {3: 'excellent', 2: 'good', 1: 'overage', 0: 'not rated'},
|
25
|
+
# watched: false,
|
26
|
+
# 'premier' 'soon'
|
27
|
+
# }
|
28
|
+
#
|
29
|
+
#
|
30
|
+
# s = Storage.new(hsh)
|
31
|
+
#
|
32
|
+
# s.cinema # => 'tiger'
|
33
|
+
# s.rate[0] # => 'not rated'
|
34
|
+
# s.watched? # => false
|
35
|
+
# s['premier'] # => soon
|
36
|
+
#
|
37
|
+
def self.create(hsh)
|
38
|
+
new hsh
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Alternative way to get stored value.
|
43
|
+
#
|
44
|
+
# How it can be used:
|
45
|
+
#
|
46
|
+
# conf[:load][:raise_on_error] # => true
|
47
|
+
# conf.load['skip_headers'] # => :yes
|
48
|
+
#
|
49
|
+
# Method will raise an ValueNotFound exception if nothing was found.
|
50
|
+
#
|
51
|
+
def [](arg)
|
52
|
+
return @storage[arg] unless @storage[arg].nil?
|
53
|
+
chaild = find_child_for_response(arg)
|
54
|
+
return chaild unless chaild.nil?
|
55
|
+
msg = "Variable with name '#{arg}' not found!"
|
56
|
+
raise ValueNotFound, msg
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# NOT IMPLEMENTED!
|
61
|
+
#
|
62
|
+
def merge(_hsh)
|
63
|
+
raise StandardError, 'not implemented!'
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
#
|
69
|
+
# Initialization.
|
70
|
+
# Additional param "#{name}" is used only for nested storage naming.
|
71
|
+
#
|
72
|
+
def initialize(hsh, name = nil)
|
73
|
+
set_initial_values name
|
74
|
+
fill_in_storage hsh
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# Slightly better way to tell about storage.
|
79
|
+
#
|
80
|
+
def to_ary
|
81
|
+
[nodes: @childs.map(&:name), variables: @storage.to_h]
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
#
|
86
|
+
#
|
87
|
+
def set_initial_values(name = nil)
|
88
|
+
@name ||= name || 'root'
|
89
|
+
@childs ||= []
|
90
|
+
@storage ||= {}
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
# For each pair "key: value" in #{"input_hsh"} we need to find a place where
|
95
|
+
# pairs can be stored. So, pairs with values === Hash is used to create new
|
96
|
+
# storages, other pairs will be stored in current storage.
|
97
|
+
#
|
98
|
+
def fill_in_storage(input_hsh)
|
99
|
+
tmp = input_hsh.dup
|
100
|
+
tmp.each_pair do |key, value|
|
101
|
+
case value
|
102
|
+
when Hash
|
103
|
+
@childs << Storage.new(value, key)
|
104
|
+
else
|
105
|
+
@storage[key] = value
|
106
|
+
register_methods_for key if key_can_be_a_method?(key)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Unfortunately some names are not allowed to be registered as methods.
|
113
|
+
# With key_can_be_a_method we should determinate is it possible to
|
114
|
+
# define a method with "#{key}" name or not.
|
115
|
+
#
|
116
|
+
def key_can_be_a_method?(key)
|
117
|
+
key_as_string_valid_integer = key.to_s.to_i.to_s == key.to_s
|
118
|
+
key.is_a?(Integer) || key_as_string_valid_integer ? false : true
|
119
|
+
end
|
120
|
+
|
121
|
+
#
|
122
|
+
# Register method with #method_name.
|
123
|
+
#
|
124
|
+
# For TrueClass or FalseClass === @storage[method_name] it will
|
125
|
+
# define additional predicate method, like:
|
126
|
+
#
|
127
|
+
# config.skip_lazy_load? # => true
|
128
|
+
#
|
129
|
+
def register_methods_for(method_name)
|
130
|
+
define_singleton_method(method_name.to_sym) do
|
131
|
+
@storage[method_name]
|
132
|
+
end
|
133
|
+
|
134
|
+
return unless [true, false].include? @storage[method_name]
|
135
|
+
define_singleton_method("#{method_name.to_sym}?") do
|
136
|
+
@storage[method_name]
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
#
|
141
|
+
# Redirect message to childen node if this node can response to the message
|
142
|
+
# otherwise try to responce itself
|
143
|
+
#
|
144
|
+
def method_missing(method, *args, &blk)
|
145
|
+
child = find_child_for_response(method)
|
146
|
+
child.nil? ? super : child
|
147
|
+
end
|
148
|
+
|
149
|
+
#
|
150
|
+
# Return TRUE if storage or any childen node can response to the message
|
151
|
+
# otherwise return FALSE
|
152
|
+
#
|
153
|
+
def respond_to?(method, include_private = false)
|
154
|
+
super || find_child_for_response(method) ? true : false
|
155
|
+
end
|
156
|
+
|
157
|
+
#
|
158
|
+
# Look for nested node with the "#{name}". Since we working with hash we
|
159
|
+
# can believe that only one nested storage can be found (equal names
|
160
|
+
# disallowed).
|
161
|
+
#
|
162
|
+
def find_child_for_response(name)
|
163
|
+
@childs.select { |chaild| chaild.name == name }.first
|
164
|
+
end
|
165
|
+
|
166
|
+
protected
|
167
|
+
|
168
|
+
# Name for node. Root node has 'root' name.
|
169
|
+
attr_reader :name
|
170
|
+
|
171
|
+
# Subnodes array (childs) for current node. Can be empty.
|
172
|
+
attr_reader :childs
|
173
|
+
|
174
|
+
# Values array (storage) for methods registered in the node.
|
175
|
+
attr_reader :storage
|
176
|
+
|
177
|
+
#
|
178
|
+
# This is supposed to be raised when we didn't found assigned
|
179
|
+
# method for value in storage.
|
180
|
+
#
|
181
|
+
class ValueNotFound < StandardError; end
|
182
|
+
end # class Settings::Storage
|
183
|
+
end # module Settings
|
data/readme.md
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
# configurates
|
2
|
+
|
3
|
+
## DESCRIPTION
|
4
|
+
|
5
|
+
customizable_configuration is a lib that provides agile way to work with configuration, which can be useful for object initialization.
|
6
|
+
|
7
|
+
## Travis CI
|
8
|
+
|
9
|
+
https://travis-ci.org/Medvedu/customizable_configuration
|
10
|
+
|
11
|
+
## SAMPLES
|
12
|
+
|
13
|
+
### SAMPLE 1. BASICS
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
conf = Settings::Config.create do
|
17
|
+
merge_storage Settings::Yaml.load('../configs/first.yml')
|
18
|
+
end
|
19
|
+
|
20
|
+
# Two ways to get an attribute:
|
21
|
+
puts conf['string'] # => 4
|
22
|
+
puts conf.string # => 4
|
23
|
+
|
24
|
+
# Any marchalized class is a valid data:
|
25
|
+
puts conf.sample.dd.to_s # => 2016-09-05 05:42:35 +0300
|
26
|
+
|
27
|
+
# Nested hash determitated as sub-storage
|
28
|
+
puts conf.sample # => {:nodes=>[:cc],
|
29
|
+
# :variables=>{:aa=>15,
|
30
|
+
# :bb=>[false, true],
|
31
|
+
# :dd=>2016-09-05 05:42:35 +0300} }
|
32
|
+
|
33
|
+
# Access by [] to predicate
|
34
|
+
puts conf[42].root? # => false
|
35
|
+
```
|
36
|
+
|
37
|
+
### SAMPLE 2. Tricks
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
## PART 1 CASE-SENSIVITY
|
41
|
+
|
42
|
+
# Warning! Keys are case-sensivity. It means that 'config' and 'Config' are
|
43
|
+
# two different keys
|
44
|
+
|
45
|
+
yaml = Settings::Config.create do
|
46
|
+
merge_storage Settings::Yaml.load('../configs/second.yml')
|
47
|
+
end
|
48
|
+
|
49
|
+
puts yaml.config # => {:nodes=>[], :variables=>{:param1=>"config/param1",
|
50
|
+
# :param2=>false, :param3=>[]}}
|
51
|
+
|
52
|
+
puts yaml.Config # => {:nodes=>[:param3], :variables=>{:param1=>"Config/param1",
|
53
|
+
# :param2=>[true]}}
|
54
|
+
|
55
|
+
## PART 2 ROOT
|
56
|
+
|
57
|
+
# Sometimes can be useful to load only a part of hash from file, so you can
|
58
|
+
# use root parameter for merge_storage method:
|
59
|
+
|
60
|
+
file = Settings::Yaml.load('../configs/second.yml')
|
61
|
+
|
62
|
+
# You can load only YAML part (root: 'config')
|
63
|
+
part1 = Settings::Config.create { merge_storage file, root: 'config' }
|
64
|
+
puts part1.param1 # => config/param1
|
65
|
+
|
66
|
+
# Or load even more deeper (root: 'Config/param3')
|
67
|
+
part2 = Settings::Config.create { merge_storage file, root: 'Config/param3' }
|
68
|
+
print part2.super_deep_array # => [1, 2, 3, 4]
|
69
|
+
```
|
70
|
+
|
71
|
+
### SAMPLE 3. PROVIDERS
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
## PART 1 ENV
|
75
|
+
|
76
|
+
dynamic = Settings::Config.create do
|
77
|
+
merge_storage Settings::ENV.load( RbConfig::CONFIG )
|
78
|
+
end
|
79
|
+
|
80
|
+
puts dynamic["RUBY_VERSION_NAME"] # => ruby-2.3.0
|
81
|
+
|
82
|
+
## PART 2 YAML
|
83
|
+
|
84
|
+
yaml = Settings::Config.create do
|
85
|
+
merge_storage Settings::Yaml.load('../configs/second.yml')
|
86
|
+
end
|
87
|
+
|
88
|
+
puts yaml[:config][:param1] # => config/param1
|
89
|
+
```
|
90
|
+
|
91
|
+
## dependencies
|
92
|
+
|
93
|
+
> Ruby 2.0.0 or higher
|
94
|
+
|
95
|
+
## License
|
96
|
+
----
|
97
|
+
|
98
|
+
MIT, see [LICENSE](./LICENSE)
|
data/spec/config_spec.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Settings::Config do
|
6
|
+
context '#create' do
|
7
|
+
it 'raises an NoBlockGiven exception when method was called without &block' do
|
8
|
+
expect { Settings::Config.create }.to raise_error Settings::Config::NoBlockGiven
|
9
|
+
end
|
10
|
+
end # context '#create'
|
11
|
+
|
12
|
+
context 'after #create' do
|
13
|
+
before :each do
|
14
|
+
@subject = described_class.create do
|
15
|
+
merge_storage Settings::ENV.load(param1: 'sample')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context '#method_missing' do
|
20
|
+
it 'redirect message call to storage when it needed' do
|
21
|
+
expect(@subject.param1).to_not be_nil
|
22
|
+
end
|
23
|
+
end # context "#method_missing"
|
24
|
+
|
25
|
+
context '#respond_to?' do
|
26
|
+
it 'redirect response_to? message call to storage when it needed' do
|
27
|
+
expect(@subject.send(:respond_to?, :param1)).to be_truthy
|
28
|
+
end
|
29
|
+
end # context 'respond_to?'
|
30
|
+
end # context 'after #create'
|
31
|
+
|
32
|
+
end # describe Settings::Config
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Settings::ENV do
|
6
|
+
let (:valid_hsh_sample) { {string: 'some_string', param: true } }
|
7
|
+
let (:not_hsh_sample) { Time.new }
|
8
|
+
|
9
|
+
context '#load' do
|
10
|
+
it 'raises an HashNotValid exception for object that not is a hash' do
|
11
|
+
expect { described_class.load(not_hsh_sample) }
|
12
|
+
.to raise_error Settings::ENV::HashNotValid
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'saves valid hash into .data field' do
|
16
|
+
obj = described_class.load valid_hsh_sample
|
17
|
+
expect(obj.data.length).to be > 0
|
18
|
+
end
|
19
|
+
end # context '#load'
|
20
|
+
end # describe Settings::ENV
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Settings::Yaml do
|
6
|
+
let(:valid_file_path) { File.join RSpec::Core::RubyProject.root, 'configs', 'first.yml' }
|
7
|
+
|
8
|
+
context 'load' do
|
9
|
+
it 'loads a hash from file' do
|
10
|
+
provider = described_class.load(valid_file_path)
|
11
|
+
expect(provider.data.length).to be > 0
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'raises FileNotFound exception when file not found' do
|
15
|
+
expect { described_class.load('file_that_can_not_exist') }
|
16
|
+
.to raise_error Settings::Yaml::FileNotFound
|
17
|
+
end
|
18
|
+
end # context 'load' do
|
19
|
+
|
20
|
+
end # describe Settings::Yaml
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Settings::Config do
|
6
|
+
context 'Provider module' do
|
7
|
+
|
8
|
+
context '#merge_storage' do
|
9
|
+
it 'raises an ProviderNotRespond exception when provider does not respond to :load method' do
|
10
|
+
expect do
|
11
|
+
Settings::Config.create { merge_storage 'string_as_provider' }
|
12
|
+
end.to raise_error Settings::Config::ProviderNotRespond
|
13
|
+
end
|
14
|
+
end # context '#merge_storage'
|
15
|
+
|
16
|
+
context '#extract_sub_hash_from' do
|
17
|
+
it 'raises an RootNotExist exception when provider can not extract a hash' do
|
18
|
+
provider = Settings::ENV.load(param1: true, para2: false)
|
19
|
+
expect do
|
20
|
+
Settings::Config.create { merge_storage(provider, root: 'wrong/path') }
|
21
|
+
end.to raise_error Settings::Provider::RootNotExist
|
22
|
+
end
|
23
|
+
end # context '#extract_sub_hash_from'
|
24
|
+
|
25
|
+
context '#validate_and_extract_hash_from' do
|
26
|
+
it 'raises an HashNotValid exception when provider getting empty hash' do
|
27
|
+
expect do
|
28
|
+
Settings::Config.create { merge_storage Settings::ENV.load({}) }
|
29
|
+
end.to raise_error Settings::Config::HashNotValid
|
30
|
+
end
|
31
|
+
end # context '#validate_and_extract_hash_from'
|
32
|
+
|
33
|
+
end # context '#provider'
|
34
|
+
end # describe Settings::Config
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Settings::Storage do
|
6
|
+
let(:hsh_sample) do
|
7
|
+
{ nested_hsh: { param1: true, param2: false },
|
8
|
+
param1: 42,
|
9
|
+
param2: 15,
|
10
|
+
predicate: true,
|
11
|
+
'special_sample' => { 55 => 'good' } }
|
12
|
+
end
|
13
|
+
|
14
|
+
subject { described_class.new(hsh_sample) }
|
15
|
+
|
16
|
+
describe 'Storage' do
|
17
|
+
context 'after #create' do
|
18
|
+
it 'registers hash key as predicate method for booleans' do
|
19
|
+
expect(subject.predicate?).to be_truthy
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'have a correct value assigned with registered method' do
|
23
|
+
expect(subject.param1).to eql(42)
|
24
|
+
end
|
25
|
+
|
26
|
+
context '#[]' do
|
27
|
+
it 'raises an ValueNotFound exception when argument for [] not valid' do
|
28
|
+
expect { subject[:param42] }
|
29
|
+
.to raise_error(Settings::Storage::ValueNotFound)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'gives value by access throw [] method' do
|
33
|
+
expect(subject[:param2]).to eql(15)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'provides chain call throw [] method' do
|
37
|
+
expect(subject[:nested_hsh][:param2]).to eql(false)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'allows to use numbers and strings as parameters' do
|
41
|
+
expect(subject['special_sample'][55]).to eql('good')
|
42
|
+
end
|
43
|
+
end # context "#[]"
|
44
|
+
|
45
|
+
context '#fill_in_storage' do
|
46
|
+
it 'registers hash key as method' do
|
47
|
+
expect(subject.send(:respond_to?, :param1)).to be_truthy
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'registers a sub-storage for sub-hash' do
|
51
|
+
expect(subject.nested_hsh).to be_an_instance_of(Settings::Storage)
|
52
|
+
end
|
53
|
+
end # context "#fill_in_storage"
|
54
|
+
|
55
|
+
context '#method_missing' do
|
56
|
+
it 'redirect message call to sub-storage' do
|
57
|
+
expect(subject.nested_hsh.param1).to_not be_nil
|
58
|
+
end
|
59
|
+
end # context "#method_missing"
|
60
|
+
|
61
|
+
context '#respond_to?' do
|
62
|
+
it 'redirect response_to? message call to sub-storage' do
|
63
|
+
expect(subject.send(:respond_to?, :nested_hsh)).to be_truthy
|
64
|
+
end
|
65
|
+
end # context 'respond_to?'
|
66
|
+
end # context 'after #create'
|
67
|
+
|
68
|
+
end # describe "Storage"
|
69
|
+
end # describe Settings::Storage
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: configurates
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kuzichev Michael
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-09-07 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email: kMedvedu@gmail.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- Gemfile
|
20
|
+
- LICENSE
|
21
|
+
- Rakefile
|
22
|
+
- lib/configurates.rb
|
23
|
+
- lib/configurates/provider.rb
|
24
|
+
- lib/configurates/provider/environment.rb
|
25
|
+
- lib/configurates/provider/yaml.rb
|
26
|
+
- lib/configurates/storage.rb
|
27
|
+
- readme.md
|
28
|
+
- spec/config_spec.rb
|
29
|
+
- spec/customizable_configuration/provider/environment_spec.rb
|
30
|
+
- spec/customizable_configuration/provider/yaml_spec.rb
|
31
|
+
- spec/customizable_configuration/provider_spec.rb
|
32
|
+
- spec/customizable_configuration/storage_spec.rb
|
33
|
+
- spec/spec_helper.rb
|
34
|
+
homepage: https://github.com/Medvedu/customizable_configuration
|
35
|
+
licenses:
|
36
|
+
- MIT
|
37
|
+
metadata: {}
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 2.0.0
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
requirements: []
|
53
|
+
rubyforge_project:
|
54
|
+
rubygems_version: 2.6.4
|
55
|
+
signing_key:
|
56
|
+
specification_version: 4
|
57
|
+
summary: configurates is a lib that provides agile way to work with configuration,
|
58
|
+
which can be useful for object initialization
|
59
|
+
test_files:
|
60
|
+
- spec/spec_helper.rb
|
61
|
+
- spec/customizable_configuration/storage_spec.rb
|
62
|
+
- spec/customizable_configuration/provider_spec.rb
|
63
|
+
- spec/customizable_configuration/provider/environment_spec.rb
|
64
|
+
- spec/customizable_configuration/provider/yaml_spec.rb
|
65
|
+
- spec/config_spec.rb
|
66
|
+
has_rdoc:
|