confo-config 1.0.0
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 +7 -0
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +35 -0
- data/Rakefile +1 -0
- data/confo.gemspec +19 -0
- data/lib/confo/collection.rb +80 -0
- data/lib/confo/concerns/options_manager.rb +184 -0
- data/lib/confo/concerns/subconfigs_manager.rb +101 -0
- data/lib/confo/config.rb +121 -0
- data/lib/confo/preconfigurator.rb +12 -0
- data/lib/confo/version.rb +3 -0
- data/lib/confo-config.rb +1 -0
- data/lib/confo.rb +43 -0
- metadata +72 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: e18009a32d37da15b10c87cff286b37be9ff6a2e
|
|
4
|
+
data.tar.gz: 0e2ad064b148ef7237ec98722afebb703dd8b2bb
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: f759fb4b9277b5f78c559cda2e270d97d2741827f2536e3cb40578d97f902eda3eaceac0906bcef6cc0b898fda0fdf1f0f105712d5fcd1570342d67eb453c548
|
|
7
|
+
data.tar.gz: d05dbef7d09242b374b5872202349dd315c7899302554e08dd143db3c30b55a5f26f7446d50e588df09bf43aebb69be38425ac63648a44debfec0f8e7cc475ae
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2015 Yaroslav Konoplov
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# ConfoConfig
|
|
2
|
+
|
|
3
|
+
## A little configuration DSL
|
|
4
|
+
```ruby
|
|
5
|
+
configuration do
|
|
6
|
+
autocomplete_limit 20
|
|
7
|
+
autocomplete_phrase_length 2
|
|
8
|
+
|
|
9
|
+
i18n do
|
|
10
|
+
available_locales [:en, :ru]
|
|
11
|
+
default_locale :en
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
list do
|
|
15
|
+
single_actions { can :update, :toggle, :delete, :preview }
|
|
16
|
+
plural_actions { can :create, :filter, :export, :search }
|
|
17
|
+
|
|
18
|
+
highlight { role admin: :red, user: :gray }
|
|
19
|
+
|
|
20
|
+
configure(:column, :avatar) { type :photo }
|
|
21
|
+
|
|
22
|
+
columns { include :name, :avatar, :role }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
form do
|
|
26
|
+
configure(:input, :avatar) { type :photo }
|
|
27
|
+
actions { can :save, :cancel }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Gemfile
|
|
33
|
+
```ruby
|
|
34
|
+
gem 'confo-config', github: 'yivo/confo-config'
|
|
35
|
+
```
|
data/Rakefile
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'bundler/gem_tasks'
|
data/confo.gemspec
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require File.expand_path('../lib/confo/version', __FILE__)
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |spec|
|
|
4
|
+
spec.name = 'confo-config'
|
|
5
|
+
spec.version = Confo::VERSION
|
|
6
|
+
spec.authors = ['Yaroslav Konoplov']
|
|
7
|
+
spec.email = ['yaroslav@inbox.com']
|
|
8
|
+
spec.summary = 'Little configuration framework'
|
|
9
|
+
spec.description = 'Little configuration framework'
|
|
10
|
+
spec.homepage = 'http://github.com/yivo/confo-config'
|
|
11
|
+
spec.license = 'MIT'
|
|
12
|
+
|
|
13
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
14
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
15
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
16
|
+
spec.require_paths = ['lib']
|
|
17
|
+
|
|
18
|
+
spec.add_dependency 'activesupport', '~> 4.0' # 4.0 <= version < 5.0
|
|
19
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
module Confo
|
|
2
|
+
class Collection
|
|
3
|
+
include Enumerable
|
|
4
|
+
|
|
5
|
+
attr_reader :behaviour_options
|
|
6
|
+
|
|
7
|
+
def initialize(behaviour_options = {}, &block)
|
|
8
|
+
@behaviour_options = behaviour_options
|
|
9
|
+
configure(&block) if block
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def define(id, options = nil, construct_options = nil, &block)
|
|
13
|
+
id = convert_id(id)
|
|
14
|
+
config = storage[id]
|
|
15
|
+
config ||= begin
|
|
16
|
+
config_class = Confo.call(self, :config_class, id, construct_options)
|
|
17
|
+
config = Confo.call(self, :construct_config, config_class, id, construct_options)
|
|
18
|
+
storage[id] = config
|
|
19
|
+
config.set(:name, id)
|
|
20
|
+
config
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
config.set(options) if options
|
|
24
|
+
config.configure(&block) if block
|
|
25
|
+
config
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def get(id)
|
|
29
|
+
define(id)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def set(id, config)
|
|
33
|
+
storage[convert_id(id)] = config
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
alias [] get
|
|
37
|
+
alias []= set
|
|
38
|
+
|
|
39
|
+
def configure(*args, &block)
|
|
40
|
+
args.present? ? define(*args).configure(&block) : instance_eval(&block)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def exists?(id)
|
|
44
|
+
storage[convert_id(id)].present?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def each(&block)
|
|
48
|
+
storage.each { |k, v| block.call(v) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_a
|
|
52
|
+
storage.values
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def to_hash
|
|
56
|
+
storage.reduce({}) do |memo, (id, instance)|
|
|
57
|
+
memo[id] = Confo.convert_to_hash(instance)
|
|
58
|
+
memo
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
protected
|
|
63
|
+
|
|
64
|
+
def config_class(config_id, config_options)
|
|
65
|
+
config_options.try(:[], :class_name).try(:safe_constantize) || Config
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def construct_config(config_class)
|
|
69
|
+
config_class.new
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def storage
|
|
73
|
+
@storage ||= ActiveSupport::OrderedHash.new
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def convert_id(id)
|
|
77
|
+
id.to_sym
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
module Confo
|
|
2
|
+
module OptionsManager
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
module ClassMethods
|
|
6
|
+
|
|
7
|
+
# Define option accessors.
|
|
8
|
+
def option_accessor(*names)
|
|
9
|
+
names.each do |name|
|
|
10
|
+
define_option_functional_accessor(name)
|
|
11
|
+
define_option_writer(name)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def define_option_functional_accessor(name)
|
|
16
|
+
define_method("#{name}") do |*args|
|
|
17
|
+
if args.size > 0
|
|
18
|
+
raw_set(name, args.first)
|
|
19
|
+
self
|
|
20
|
+
else
|
|
21
|
+
raw_get(name)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def define_option_writer(name)
|
|
27
|
+
define_method("#{name}=") do |value|
|
|
28
|
+
raw_set(name, value)
|
|
29
|
+
self
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Returns option value:
|
|
35
|
+
# obj.get(:option) => 'value'
|
|
36
|
+
# obj.get('option') => 'value'
|
|
37
|
+
#
|
|
38
|
+
# obj.set :option, -> { 'value' }
|
|
39
|
+
# obj.get(:option) => 'value'
|
|
40
|
+
#
|
|
41
|
+
# obj.set :option, -> (arg) { 'value' }
|
|
42
|
+
# obj.get(:option) => -> (arg) { 'value' }
|
|
43
|
+
def get(option)
|
|
44
|
+
public_get(option)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Alias for +get+.
|
|
48
|
+
alias [] get
|
|
49
|
+
|
|
50
|
+
protected
|
|
51
|
+
|
|
52
|
+
# Method to get an option.
|
|
53
|
+
# If there is an option accessor defined then it will be used.
|
|
54
|
+
# In other cases +raw_get+ will be used.
|
|
55
|
+
def public_get(option)
|
|
56
|
+
respond_to?(option) ? send(option) : raw_get(option)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Internal method to get an option.
|
|
60
|
+
# If value is callable without arguments
|
|
61
|
+
# it will be called and result will be returned.
|
|
62
|
+
def raw_get(option)
|
|
63
|
+
value = storage[option]
|
|
64
|
+
Confo.callable_without_arguments?(value) ? value.call : value
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
public
|
|
68
|
+
|
|
69
|
+
# Sets option:
|
|
70
|
+
# obj.set(:option, 'value')
|
|
71
|
+
# obj.set('option', 'value')
|
|
72
|
+
# obj.set({ foo: '1', bar: '2', baz: -> { 3 } })
|
|
73
|
+
def set(arg, *args)
|
|
74
|
+
if arg.kind_of?(Hash)
|
|
75
|
+
arg.each { |k, v| public_set(k, v) }
|
|
76
|
+
elsif args.size > 0
|
|
77
|
+
public_set(arg, args.first)
|
|
78
|
+
end
|
|
79
|
+
self
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Alias for +set+.
|
|
83
|
+
alias []= set
|
|
84
|
+
|
|
85
|
+
protected
|
|
86
|
+
|
|
87
|
+
# Method to set an option.
|
|
88
|
+
# If there is an option accessor defined then it will be used.
|
|
89
|
+
# In other cases +raw_set+ will be used.
|
|
90
|
+
def public_set(option, value)
|
|
91
|
+
method = "#{option}="
|
|
92
|
+
respond_to?(method) ? send(method, value) : raw_set(option, value)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Internal method to set option.
|
|
96
|
+
def raw_set(option, value)
|
|
97
|
+
storage[option] = value
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
public
|
|
101
|
+
|
|
102
|
+
# Sets option only if it is not set yet:
|
|
103
|
+
# obj.set_at_first(:option, 1)
|
|
104
|
+
# obj.get(:option) => 1
|
|
105
|
+
# obj.set_at_first(:option, 2)
|
|
106
|
+
# obj.get(:option) => 1
|
|
107
|
+
def set_at_first(*args)
|
|
108
|
+
set?(*args)
|
|
109
|
+
self
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Alias for +set_at_first+.
|
|
113
|
+
alias init set_at_first
|
|
114
|
+
|
|
115
|
+
# Option accessor in functional style:
|
|
116
|
+
# obj.option(:option, 'value')
|
|
117
|
+
# obj.option(:option) => 'value'
|
|
118
|
+
def option(option, *args)
|
|
119
|
+
args.size > 0 ? set(option, args.first) : get(option)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Checks if option is set.
|
|
123
|
+
# Works similar to set if value passed but sets only uninitialized options.
|
|
124
|
+
def set?(arg, *rest_args)
|
|
125
|
+
if arg.kind_of?(Hash)
|
|
126
|
+
arg.each { |k, v| set(k, v) unless set?(k) }
|
|
127
|
+
nil
|
|
128
|
+
elsif rest_args.size > 0
|
|
129
|
+
set(arg, rest_args.first) unless set?(arg)
|
|
130
|
+
true
|
|
131
|
+
else
|
|
132
|
+
storage.has_key?(arg)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# If you expect computed value you can pass arguments to it:
|
|
137
|
+
# obj.set :calculator, -> (num) { num * 2 }
|
|
138
|
+
# obj.result_of :calculator, 2 => 4
|
|
139
|
+
def result_of(option, *args)
|
|
140
|
+
Confo.result_of(get(option), *args)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Unsets option.
|
|
144
|
+
def unset(option)
|
|
145
|
+
storage.delete(option)
|
|
146
|
+
self
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Returns option names as array of symbols.
|
|
150
|
+
def keys
|
|
151
|
+
storage.reduce([]) do |memo, k, v|
|
|
152
|
+
memo << k.to_sym
|
|
153
|
+
memo
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Returns option values as array.
|
|
158
|
+
def values
|
|
159
|
+
storage.values
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Returns all options at once.
|
|
163
|
+
# obj.options => { option: 'value' }
|
|
164
|
+
def options
|
|
165
|
+
options = storage.dup
|
|
166
|
+
options.each { |k, v| options[k] = get(k) }
|
|
167
|
+
options
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
protected
|
|
171
|
+
|
|
172
|
+
def storage
|
|
173
|
+
@storage ||= OptionsStorage.new
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
class OptionsStorage < Hash
|
|
178
|
+
def to_hash
|
|
179
|
+
new_hash = {}
|
|
180
|
+
each { |k, v| new_hash[k.kind_of?(String) ? k.to_sym : k] = Confo.convert_to_hash(v) }
|
|
181
|
+
Hash.new(default).merge!(new_hash)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
module Confo
|
|
2
|
+
module SubconfigsManager
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
module ClassMethods
|
|
6
|
+
def includes_config(arg, options = {})
|
|
7
|
+
@subconfigs_options ||= {}
|
|
8
|
+
|
|
9
|
+
# Support different syntaxes:
|
|
10
|
+
# includes_config :actions
|
|
11
|
+
# includes_config of: :actions
|
|
12
|
+
# includes_config for: :actions
|
|
13
|
+
name = (arg.kind_of?(Hash) ? arg[:of] || arg[:for] : arg).to_sym
|
|
14
|
+
new_options = @subconfigs_options[name]
|
|
15
|
+
new_options ||= { name: name, fallback_class_name: 'Confo::Config' }
|
|
16
|
+
new_options.merge!(options)
|
|
17
|
+
|
|
18
|
+
@subconfigs_options[name] = new_options
|
|
19
|
+
|
|
20
|
+
define_subconfig_reader(name, new_options)
|
|
21
|
+
self
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def subconfigs_options
|
|
25
|
+
all_options = @subconfigs_options ? @subconfigs_options.dup : {}
|
|
26
|
+
if superclass.respond_to?(:subconfigs_options)
|
|
27
|
+
superclass.subconfigs_options.each do |name, options|
|
|
28
|
+
new_options = {}
|
|
29
|
+
new_options.merge!(options[name]) if options[name]
|
|
30
|
+
new_options.merge!(options)
|
|
31
|
+
options[name] = new_options
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
all_options
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def define_subconfig_reader(subconfig_name, subconfig_options)
|
|
38
|
+
define_method(subconfig_name) do |options = nil, overrides = nil, &block|
|
|
39
|
+
subconfig_internal(subconfig_name, options, overrides, &block)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def subconfigs
|
|
45
|
+
unless @all_subconfigs_loaded
|
|
46
|
+
self.class.subconfigs_options.each { |name, options| subconfig(name) }
|
|
47
|
+
@all_subconfigs_loaded = true
|
|
48
|
+
end
|
|
49
|
+
subconfig_instances
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def subconfig(subconfig_name, options = nil, overrides = nil, &block)
|
|
53
|
+
respond_to?(subconfig_name) ?
|
|
54
|
+
send(subconfig_name, options, overrides, &block) :
|
|
55
|
+
subconfig_internal(subconfig_name, options, overrides, &block)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def subconfig_exists?(subconfig_name)
|
|
59
|
+
subconfig_instances.exists?(subconfig_name)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
protected
|
|
63
|
+
|
|
64
|
+
def subconfig_instances
|
|
65
|
+
@subconfig_instances ||= Collection.new
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def subconfig_internal(subconfig_name, options = nil, overrides = nil, &block)
|
|
69
|
+
unless subconfig_exists?(subconfig_name)
|
|
70
|
+
subconfig_options = self.class.subconfigs_options[subconfig_name].try(:dup) || {}
|
|
71
|
+
subconfig_options.merge!(overrides) if overrides
|
|
72
|
+
|
|
73
|
+
subconfig_class = Confo.call(self, :subconfig_class, subconfig_name, subconfig_options)
|
|
74
|
+
subconfig_instance = Confo.call(self, :construct_subconfig, subconfig_class, subconfig_options)
|
|
75
|
+
|
|
76
|
+
subconfig_instance.set(:name, subconfig_name) unless subconfig_instance.kind_of?(Collection)
|
|
77
|
+
subconfig_instances.set(subconfig_name, subconfig_instance)
|
|
78
|
+
end
|
|
79
|
+
subconfig_instance = subconfig_instances.get(subconfig_name)
|
|
80
|
+
subconfig_instance.set(options) if options
|
|
81
|
+
subconfig_instance.configure(&block) if block
|
|
82
|
+
subconfig_instance
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def construct_subconfig(subconfig_class)
|
|
86
|
+
subconfig_class.new
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def subconfig_class(subconfig_name, subconfig_options)
|
|
90
|
+
if class_name = subconfig_options[:class_name]
|
|
91
|
+
class_name.to_s.camelize
|
|
92
|
+
else
|
|
93
|
+
guess_subconfig_class_name(subconfig_name)
|
|
94
|
+
end.safe_constantize || subconfig_options.fetch(:fallback_class_name, 'Confo::Config').constantize
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def guess_subconfig_class_name(subconfig_name)
|
|
98
|
+
"#{subconfig_name.to_s.camelize}Config"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
data/lib/confo/config.rb
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
require_relative '../confo.rb'
|
|
2
|
+
|
|
3
|
+
module Confo
|
|
4
|
+
class Config
|
|
5
|
+
include OptionsManager
|
|
6
|
+
include SubconfigsManager
|
|
7
|
+
|
|
8
|
+
attr_reader :behaviour_options
|
|
9
|
+
|
|
10
|
+
def initialize(behaviour_options = {}, &block)
|
|
11
|
+
@behaviour_options = behaviour_options
|
|
12
|
+
preconfigure
|
|
13
|
+
configure(&block) if block
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def configure(*args, &block)
|
|
17
|
+
case args.size
|
|
18
|
+
|
|
19
|
+
# Current config configuration:
|
|
20
|
+
# object.configure { }
|
|
21
|
+
when 0
|
|
22
|
+
instance_eval(&block) if block
|
|
23
|
+
self
|
|
24
|
+
|
|
25
|
+
when 1, 2, 3
|
|
26
|
+
arg1, arg2, arg3 = args
|
|
27
|
+
arg1_hash = arg1.kind_of?(Hash)
|
|
28
|
+
|
|
29
|
+
# Hash-based collection syntax:
|
|
30
|
+
# object.configure(property: :id) { }
|
|
31
|
+
# object.configure(property: :id, {option: :value}) { }
|
|
32
|
+
#
|
|
33
|
+
# Full definition syntax:
|
|
34
|
+
# object.configure(:property, :id) { }
|
|
35
|
+
# object.configure(:property, :id, {option: :value}) { }
|
|
36
|
+
if arg1_hash || (args.size == 2 && arg2.kind_of?(Hash) == false)
|
|
37
|
+
subconfig_name = (arg1_hash ? arg1.keys.first : arg1).to_s.pluralize
|
|
38
|
+
config_id = arg1_hash ? arg1.values.first : arg2
|
|
39
|
+
options = arg1_hash ? arg2 : arg3
|
|
40
|
+
subconfig(subconfig_name, options, fallback_class_name: 'Confo::Collection')
|
|
41
|
+
.configure(config_id, &block)
|
|
42
|
+
else
|
|
43
|
+
|
|
44
|
+
# Subconfig configuration:
|
|
45
|
+
# object.configure(:description)
|
|
46
|
+
# object.configure(:description, {option: :value})
|
|
47
|
+
subconfig(arg1, arg2, &block)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
else self
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def method_missing(name, *args, &block)
|
|
55
|
+
case args.size
|
|
56
|
+
when 0
|
|
57
|
+
if block
|
|
58
|
+
# Wants to configure subconfig:
|
|
59
|
+
# object.description { }
|
|
60
|
+
subconfig(name, &block)
|
|
61
|
+
else
|
|
62
|
+
|
|
63
|
+
# Wants one of the following:
|
|
64
|
+
# - access subconfig
|
|
65
|
+
# - access option
|
|
66
|
+
subconfig_exists?(name) ? subconfig(name) : option(normalize_option(name))
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
when 1
|
|
70
|
+
arg = args.first
|
|
71
|
+
|
|
72
|
+
# Wants to access collection:
|
|
73
|
+
# object.properties :id { }
|
|
74
|
+
if (arg.is_a?(String) || arg.is_a?(Symbol)) && subconfig_exists?(name)
|
|
75
|
+
subconfig(name.to_s.pluralize, arg, &block)
|
|
76
|
+
else
|
|
77
|
+
|
|
78
|
+
# Wants to access option:
|
|
79
|
+
# object.cache = :none
|
|
80
|
+
# object.cache :none
|
|
81
|
+
option(normalize_option(name), arg)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
else
|
|
85
|
+
option(normalize_option(name), *args)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def to_hash
|
|
90
|
+
{}.merge!(options.to_hash).merge!(subconfigs.to_hash)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
protected
|
|
94
|
+
|
|
95
|
+
def preconfigure
|
|
96
|
+
preconfigurator_class = lookup_preconfigurator_class
|
|
97
|
+
if preconfigurator_class
|
|
98
|
+
preconfigurator_class.instance.preconfigure(self)
|
|
99
|
+
true
|
|
100
|
+
else
|
|
101
|
+
false
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def lookup_preconfigurator_class
|
|
106
|
+
guess_preconfigurator_class_name.safe_constantize
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def configurable_component_name
|
|
110
|
+
self.class.name.demodulize.sub(/Config\Z/, '')
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def guess_preconfigurator_class_name
|
|
114
|
+
"#{configurable_component_name}Preconfigurator"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def normalize_option(name)
|
|
118
|
+
name.to_s.sub(/=+\Z/, '').to_sym
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
data/lib/confo-config.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require_relative 'confo.rb'
|
data/lib/confo.rb
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'active_support/all'
|
|
2
|
+
require 'confo/version'
|
|
3
|
+
require 'confo/concerns/options_manager'
|
|
4
|
+
require 'confo/concerns/subconfigs_manager'
|
|
5
|
+
require 'confo/config'
|
|
6
|
+
require 'confo/collection'
|
|
7
|
+
require 'confo/preconfigurator'
|
|
8
|
+
|
|
9
|
+
module Confo
|
|
10
|
+
class << self
|
|
11
|
+
def result_of(value, *args)
|
|
12
|
+
if value.respond_to?(:call)
|
|
13
|
+
_args_ = value.arity < 0 ? args : args[0...value.arity]
|
|
14
|
+
value.call(*args)
|
|
15
|
+
else
|
|
16
|
+
value
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def callable_without_arguments?(obj)
|
|
21
|
+
obj.respond_to?(:call) && (!obj.respond_to?(:arity) || obj.arity == 0)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def convert_to_hash(value)
|
|
25
|
+
if value.is_a?(Hash)
|
|
26
|
+
value.to_hash
|
|
27
|
+
elsif value.is_a?(Array)
|
|
28
|
+
value.map { |e| convert_to_hash(e) }
|
|
29
|
+
else
|
|
30
|
+
value.respond_to?(:to_hash) ? value.to_hash : value
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def call_method_with_floating_arguments(object, method, *args)
|
|
35
|
+
callable = object.method(method)
|
|
36
|
+
arity = callable.arity
|
|
37
|
+
resized_args = arity < 0 ? args : args[0...arity]
|
|
38
|
+
callable.call(*resized_args)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
alias call call_method_with_floating_arguments
|
|
42
|
+
end
|
|
43
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: confo-config
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Yaroslav Konoplov
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2015-09-04 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activesupport
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '4.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '4.0'
|
|
27
|
+
description: Little configuration framework
|
|
28
|
+
email:
|
|
29
|
+
- yaroslav@inbox.com
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- ".gitignore"
|
|
35
|
+
- Gemfile
|
|
36
|
+
- LICENSE.txt
|
|
37
|
+
- README.md
|
|
38
|
+
- Rakefile
|
|
39
|
+
- confo.gemspec
|
|
40
|
+
- lib/confo-config.rb
|
|
41
|
+
- lib/confo.rb
|
|
42
|
+
- lib/confo/collection.rb
|
|
43
|
+
- lib/confo/concerns/options_manager.rb
|
|
44
|
+
- lib/confo/concerns/subconfigs_manager.rb
|
|
45
|
+
- lib/confo/config.rb
|
|
46
|
+
- lib/confo/preconfigurator.rb
|
|
47
|
+
- lib/confo/version.rb
|
|
48
|
+
homepage: http://github.com/yivo/confo-config
|
|
49
|
+
licenses:
|
|
50
|
+
- MIT
|
|
51
|
+
metadata: {}
|
|
52
|
+
post_install_message:
|
|
53
|
+
rdoc_options: []
|
|
54
|
+
require_paths:
|
|
55
|
+
- lib
|
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
|
+
requirements:
|
|
63
|
+
- - ">="
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: '0'
|
|
66
|
+
requirements: []
|
|
67
|
+
rubyforge_project:
|
|
68
|
+
rubygems_version: 2.4.8
|
|
69
|
+
signing_key:
|
|
70
|
+
specification_version: 4
|
|
71
|
+
summary: Little configuration framework
|
|
72
|
+
test_files: []
|