strada 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +306 -0
- data/Gemfile +12 -0
- data/README.md +59 -0
- data/Rakefile +47 -0
- data/lib/strada/adapter/json.rb +28 -0
- data/lib/strada/adapter/toml.rb +29 -0
- data/lib/strada/adapter/yaml.rb +29 -0
- data/lib/strada/config_struct.rb +89 -0
- data/lib/strada/version.rb +5 -0
- data/lib/strada.rb +181 -0
- data/strada.gemspec +17 -0
- metadata +55 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d79487b0f3611692c622efcdb8c4eb7114b313f7f65b5683e4a9a772b7d5294f
|
4
|
+
data.tar.gz: 5a3447b412eea2ab0e8db8520aa0c845a994221445e55ee2af7ce45009927a79
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 19b6eeabb5287a7a260dc821e979e5b10669aac42b2423aa4e23e3ad2ee93b33e0e96db98ae559ac9c9a0ad8d6997a498f1750cc47b9d25ae5fd8acc56212256
|
7
|
+
data.tar.gz: 8d4e2b5e207db3bb4676aca93a7d0e48025aba2a61e5d6e63778ef3d99c8bda74e0a9a2fe90beaccbfa58e55c07c403e81a74b3fdbfccd3a876fa3bec3a52e1b
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,306 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-minitest
|
3
|
+
- rubocop-packaging
|
4
|
+
- rubocop-performance
|
5
|
+
- rubocop-rails
|
6
|
+
|
7
|
+
AllCops:
|
8
|
+
TargetRubyVersion: 2.7
|
9
|
+
# RuboCop has a bunch of cops enabled by default. This setting tells RuboCop
|
10
|
+
# to ignore them, so only the ones explicitly set in this file are enabled.
|
11
|
+
DisabledByDefault: true
|
12
|
+
SuggestExtensions: false
|
13
|
+
Exclude:
|
14
|
+
- '**/tmp/**/*'
|
15
|
+
- '**/templates/**/*'
|
16
|
+
- '**/vendor/**/*'
|
17
|
+
- 'actionpack/lib/action_dispatch/journey/parser.rb'
|
18
|
+
- 'actionmailbox/test/dummy/**/*'
|
19
|
+
- 'actiontext/test/dummy/**/*'
|
20
|
+
- '**/node_modules/**/*'
|
21
|
+
|
22
|
+
Performance:
|
23
|
+
Exclude:
|
24
|
+
- '**/test/**/*'
|
25
|
+
|
26
|
+
# Prefer assert_not over assert !
|
27
|
+
Rails/AssertNot:
|
28
|
+
Include:
|
29
|
+
- '**/test/**/*'
|
30
|
+
|
31
|
+
# Prefer assert_not_x over refute_x
|
32
|
+
Rails/RefuteMethods:
|
33
|
+
Include:
|
34
|
+
- '**/test/**/*'
|
35
|
+
|
36
|
+
Rails/IndexBy:
|
37
|
+
Enabled: true
|
38
|
+
|
39
|
+
Rails/IndexWith:
|
40
|
+
Enabled: true
|
41
|
+
|
42
|
+
# Prefer &&/|| over and/or.
|
43
|
+
Style/AndOr:
|
44
|
+
Enabled: true
|
45
|
+
|
46
|
+
# Align `when` with `case`.
|
47
|
+
Layout/CaseIndentation:
|
48
|
+
Enabled: true
|
49
|
+
|
50
|
+
Layout/ClosingHeredocIndentation:
|
51
|
+
Enabled: true
|
52
|
+
|
53
|
+
Layout/ClosingParenthesisIndentation:
|
54
|
+
Enabled: true
|
55
|
+
|
56
|
+
# Align comments with method definitions.
|
57
|
+
Layout/CommentIndentation:
|
58
|
+
Enabled: true
|
59
|
+
|
60
|
+
Layout/ElseAlignment:
|
61
|
+
Enabled: true
|
62
|
+
|
63
|
+
# Align `end` with the matching keyword or starting expression except for
|
64
|
+
# assignments, where it should be aligned with the LHS.
|
65
|
+
Layout/EndAlignment:
|
66
|
+
Enabled: true
|
67
|
+
EnforcedStyleAlignWith: variable
|
68
|
+
AutoCorrect: true
|
69
|
+
|
70
|
+
Layout/EndOfLine:
|
71
|
+
Enabled: true
|
72
|
+
|
73
|
+
Layout/EmptyLineAfterMagicComment:
|
74
|
+
Enabled: true
|
75
|
+
|
76
|
+
Layout/EmptyLinesAroundAccessModifier:
|
77
|
+
Enabled: true
|
78
|
+
EnforcedStyle: only_before
|
79
|
+
|
80
|
+
Layout/EmptyLinesAroundBlockBody:
|
81
|
+
Enabled: true
|
82
|
+
|
83
|
+
# In a regular class definition, no empty lines around the body.
|
84
|
+
Layout/EmptyLinesAroundClassBody:
|
85
|
+
Enabled: true
|
86
|
+
|
87
|
+
# In a regular method definition, no empty lines around the body.
|
88
|
+
Layout/EmptyLinesAroundMethodBody:
|
89
|
+
Enabled: true
|
90
|
+
|
91
|
+
# In a regular module definition, no empty lines around the body.
|
92
|
+
Layout/EmptyLinesAroundModuleBody:
|
93
|
+
Enabled: true
|
94
|
+
|
95
|
+
# Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }.
|
96
|
+
Style/HashSyntax:
|
97
|
+
Enabled: true
|
98
|
+
|
99
|
+
# Method definitions after `private` or `protected` isolated calls need one
|
100
|
+
# extra level of indentation.
|
101
|
+
Layout/IndentationConsistency:
|
102
|
+
Enabled: true
|
103
|
+
EnforcedStyle: indented_internal_methods
|
104
|
+
|
105
|
+
# Two spaces, no tabs (for indentation).
|
106
|
+
Layout/IndentationWidth:
|
107
|
+
Enabled: true
|
108
|
+
|
109
|
+
Layout/LeadingCommentSpace:
|
110
|
+
Enabled: true
|
111
|
+
|
112
|
+
Layout/SpaceAfterColon:
|
113
|
+
Enabled: true
|
114
|
+
|
115
|
+
Layout/SpaceAfterComma:
|
116
|
+
Enabled: true
|
117
|
+
|
118
|
+
Layout/SpaceAfterSemicolon:
|
119
|
+
Enabled: true
|
120
|
+
|
121
|
+
Layout/SpaceAroundEqualsInParameterDefault:
|
122
|
+
Enabled: true
|
123
|
+
|
124
|
+
Layout/SpaceAroundKeyword:
|
125
|
+
Enabled: true
|
126
|
+
|
127
|
+
Layout/SpaceAroundOperators:
|
128
|
+
Enabled: true
|
129
|
+
|
130
|
+
Layout/SpaceBeforeComma:
|
131
|
+
Enabled: true
|
132
|
+
|
133
|
+
Layout/SpaceBeforeComment:
|
134
|
+
Enabled: true
|
135
|
+
|
136
|
+
Layout/SpaceBeforeFirstArg:
|
137
|
+
Enabled: true
|
138
|
+
|
139
|
+
Style/DefWithParentheses:
|
140
|
+
Enabled: true
|
141
|
+
|
142
|
+
# Defining a method with parameters needs parentheses.
|
143
|
+
Style/MethodDefParentheses:
|
144
|
+
Enabled: true
|
145
|
+
|
146
|
+
Style/ExplicitBlockArgument:
|
147
|
+
Enabled: true
|
148
|
+
|
149
|
+
Style/FrozenStringLiteralComment:
|
150
|
+
Enabled: true
|
151
|
+
EnforcedStyle: always
|
152
|
+
Exclude:
|
153
|
+
- 'actionview/test/**/*.builder'
|
154
|
+
- 'actionview/test/**/*.ruby'
|
155
|
+
- 'actionpack/test/**/*.builder'
|
156
|
+
- 'actionpack/test/**/*.ruby'
|
157
|
+
- 'activestorage/db/migrate/**/*.rb'
|
158
|
+
- 'activestorage/db/update_migrate/**/*.rb'
|
159
|
+
- 'actionmailbox/db/migrate/**/*.rb'
|
160
|
+
- 'actiontext/db/migrate/**/*.rb'
|
161
|
+
|
162
|
+
Style/RedundantFreeze:
|
163
|
+
Enabled: true
|
164
|
+
|
165
|
+
# Use `foo {}` not `foo{}`.
|
166
|
+
Layout/SpaceBeforeBlockBraces:
|
167
|
+
Enabled: true
|
168
|
+
|
169
|
+
# Use `foo { bar }` not `foo {bar}`.
|
170
|
+
Layout/SpaceInsideBlockBraces:
|
171
|
+
Enabled: true
|
172
|
+
EnforcedStyleForEmptyBraces: space
|
173
|
+
|
174
|
+
# Use `{ a: 1 }` not `{a:1}`.
|
175
|
+
Layout/SpaceInsideHashLiteralBraces:
|
176
|
+
Enabled: true
|
177
|
+
|
178
|
+
Layout/SpaceInsideParens:
|
179
|
+
Enabled: true
|
180
|
+
|
181
|
+
# Check quotes usage according to lint rule below.
|
182
|
+
Style/StringLiterals:
|
183
|
+
Enabled: true
|
184
|
+
EnforcedStyle: double_quotes
|
185
|
+
|
186
|
+
# Detect hard tabs, no hard tabs.
|
187
|
+
Layout/IndentationStyle:
|
188
|
+
Enabled: true
|
189
|
+
|
190
|
+
# Empty lines should not have any spaces.
|
191
|
+
Layout/TrailingEmptyLines:
|
192
|
+
Enabled: true
|
193
|
+
|
194
|
+
# No trailing whitespace.
|
195
|
+
Layout/TrailingWhitespace:
|
196
|
+
Enabled: true
|
197
|
+
|
198
|
+
# Use quotes for string literals when they are enough.
|
199
|
+
Style/RedundantPercentQ:
|
200
|
+
Enabled: true
|
201
|
+
|
202
|
+
Lint/AmbiguousOperator:
|
203
|
+
Enabled: true
|
204
|
+
|
205
|
+
Lint/AmbiguousRegexpLiteral:
|
206
|
+
Enabled: true
|
207
|
+
|
208
|
+
Lint/DuplicateRequire:
|
209
|
+
Enabled: true
|
210
|
+
|
211
|
+
Lint/DuplicateMethods:
|
212
|
+
Enabled: true
|
213
|
+
|
214
|
+
Lint/ErbNewArguments:
|
215
|
+
Enabled: true
|
216
|
+
|
217
|
+
Lint/EnsureReturn:
|
218
|
+
Enabled: true
|
219
|
+
|
220
|
+
# Use my_method(my_arg) not my_method( my_arg ) or my_method my_arg.
|
221
|
+
Lint/RequireParentheses:
|
222
|
+
Enabled: true
|
223
|
+
|
224
|
+
Lint/RedundantStringCoercion:
|
225
|
+
Enabled: true
|
226
|
+
|
227
|
+
Lint/UriEscapeUnescape:
|
228
|
+
Enabled: true
|
229
|
+
|
230
|
+
Lint/UselessAssignment:
|
231
|
+
Enabled: true
|
232
|
+
|
233
|
+
Lint/DeprecatedClassMethods:
|
234
|
+
Enabled: true
|
235
|
+
|
236
|
+
Style/ParenthesesAroundCondition:
|
237
|
+
Enabled: true
|
238
|
+
|
239
|
+
Style/HashTransformKeys:
|
240
|
+
Enabled: true
|
241
|
+
|
242
|
+
Style/HashTransformValues:
|
243
|
+
Enabled: true
|
244
|
+
|
245
|
+
Style/RedundantBegin:
|
246
|
+
Enabled: true
|
247
|
+
|
248
|
+
Style/RedundantReturn:
|
249
|
+
Enabled: true
|
250
|
+
AllowMultipleReturnValues: true
|
251
|
+
|
252
|
+
Style/RedundantRegexpEscape:
|
253
|
+
Enabled: true
|
254
|
+
|
255
|
+
Style/Semicolon:
|
256
|
+
Enabled: true
|
257
|
+
AllowAsExpressionSeparator: true
|
258
|
+
|
259
|
+
# Prefer Foo.method over Foo::method
|
260
|
+
Style/ColonMethodCall:
|
261
|
+
Enabled: true
|
262
|
+
|
263
|
+
Style/TrivialAccessors:
|
264
|
+
Enabled: true
|
265
|
+
|
266
|
+
Performance/BindCall:
|
267
|
+
Enabled: true
|
268
|
+
|
269
|
+
Performance/FlatMap:
|
270
|
+
Enabled: true
|
271
|
+
|
272
|
+
Performance/MapCompact:
|
273
|
+
Enabled: true
|
274
|
+
|
275
|
+
Performance/SelectMap:
|
276
|
+
Enabled: true
|
277
|
+
|
278
|
+
Performance/RedundantMerge:
|
279
|
+
Enabled: true
|
280
|
+
|
281
|
+
Performance/StartWith:
|
282
|
+
Enabled: true
|
283
|
+
|
284
|
+
Performance/EndWith:
|
285
|
+
Enabled: true
|
286
|
+
|
287
|
+
Performance/RegexpMatch:
|
288
|
+
Enabled: true
|
289
|
+
|
290
|
+
Performance/ReverseEach:
|
291
|
+
Enabled: true
|
292
|
+
|
293
|
+
Performance/StringReplacement:
|
294
|
+
Enabled: true
|
295
|
+
|
296
|
+
Performance/UnfreezeString:
|
297
|
+
Enabled: true
|
298
|
+
|
299
|
+
Performance/DeletePrefix:
|
300
|
+
Enabled: true
|
301
|
+
|
302
|
+
Performance/DeleteSuffix:
|
303
|
+
Enabled: true
|
304
|
+
|
305
|
+
Minitest/UnreachableAssertion:
|
306
|
+
Enabled: true
|
data/Gemfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
gemspec
|
3
|
+
|
4
|
+
group :rubocop do
|
5
|
+
gem "rubocop", require: false
|
6
|
+
gem "rubocop-minitest", require: false
|
7
|
+
gem "rubocop-packaging", require: false
|
8
|
+
gem "rubocop-performance", require: false
|
9
|
+
gem "rubocop-rails", require: false
|
10
|
+
end
|
11
|
+
|
12
|
+
gem "rake"
|
data/README.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Strada
|
2
|
+
Configuration library for ruby with YAML/JSON/TOML backends with unified object
|
3
|
+
access
|
4
|
+
|
5
|
+
## Install
|
6
|
+
```
|
7
|
+
% gem install Strada
|
8
|
+
```
|
9
|
+
|
10
|
+
## Use
|
11
|
+
### Simple
|
12
|
+
```
|
13
|
+
require 'Strada'
|
14
|
+
cfg = Strada.cfg
|
15
|
+
port = cfg.server.port
|
16
|
+
user = cfg.auth.user
|
17
|
+
pw = cfg.auth.password
|
18
|
+
```
|
19
|
+
It tried to detect your software name via caller_locations if no ':name'
|
20
|
+
argument was given.
|
21
|
+
It automatically loads /etc/name/config and ~/.config/name/config and merges
|
22
|
+
them together.
|
23
|
+
|
24
|
+
### Advanced
|
25
|
+
```
|
26
|
+
require 'Strada'
|
27
|
+
Strada = Strada.new name: 'mykewlapp',
|
28
|
+
default: {'poop'=>'xyzzy'},
|
29
|
+
adapter: 'yaml',
|
30
|
+
usrdir: '/home/app/config/',
|
31
|
+
sysdir: '/System/config/',
|
32
|
+
load: false
|
33
|
+
Strada.default.poop2 = [1, 2, 3, 4]
|
34
|
+
Strada.default.starship.poopers = 42
|
35
|
+
Strada.load :user
|
36
|
+
if Strada.user.empty?
|
37
|
+
Strada.user = Strada.default
|
38
|
+
Strada.save :user
|
39
|
+
end
|
40
|
+
Strada.load # load+merges cfg, takes argument :default, :system, :user
|
41
|
+
Strada.cfg # merged default + system + user (merged on load)
|
42
|
+
Strada.default # default only
|
43
|
+
Strada.system # system only
|
44
|
+
Strada.user # user only
|
45
|
+
```
|
46
|
+
|
47
|
+
## Reserved methods
|
48
|
+
|
49
|
+
* each - iterate all config keys in current level
|
50
|
+
* has_key?(arg) - check if current level has key arg
|
51
|
+
* [arg] - fetch arg (useful for non-literal retrieval, instead of using #send)
|
52
|
+
* key? - all keys have question mark version reserved, checks if key exists+true (true), exists+false (false), not-exists (nil)
|
53
|
+
+ all object class methods
|
54
|
+
|
55
|
+
## TODO
|
56
|
+
|
57
|
+
* should I add feature to raise on unconfigured/unset?
|
58
|
+
* should I always merge to 'cfg' when default/system/config is set?
|
59
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
begin
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'bundler'
|
4
|
+
#Bundler.setup
|
5
|
+
rescue LoadError
|
6
|
+
warn 'bunler missing'
|
7
|
+
exit 42
|
8
|
+
end
|
9
|
+
|
10
|
+
gemspec = eval(File.read(Dir['*.gemspec'].first))
|
11
|
+
file = [gemspec.name, gemspec.version].join('-') + '.gem'
|
12
|
+
|
13
|
+
desc 'Validate gemspec'
|
14
|
+
task :gemspec do
|
15
|
+
gemspec.validate
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'Run minitest'
|
19
|
+
task :test do
|
20
|
+
Rake::TestTask.new do |t|
|
21
|
+
t.libs.push "lib"
|
22
|
+
t.test_files = FileList['spec/*_spec.rb']
|
23
|
+
t.verbose = true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Build gem'
|
28
|
+
task :build do
|
29
|
+
system "gem build #{gemspec.name}.gemspec"
|
30
|
+
FileUtils.mkdir_p 'gems'
|
31
|
+
FileUtils.mv file, 'gems'
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'Install gem'
|
35
|
+
task :install => :build do
|
36
|
+
system "sudo -E sh -c \'umask 022; gem install gems/#{file}\'"
|
37
|
+
end
|
38
|
+
|
39
|
+
desc 'Remove gems'
|
40
|
+
task :clean do
|
41
|
+
FileUtils.rm_rf 'gems'
|
42
|
+
end
|
43
|
+
|
44
|
+
desc 'Push to rubygems'
|
45
|
+
task :push do
|
46
|
+
system "gem push gems/#{file}"
|
47
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Strada
|
4
|
+
def to_json(config)
|
5
|
+
Adapter::JSON.to config._config_to_hash
|
6
|
+
end
|
7
|
+
|
8
|
+
def from_json(json)
|
9
|
+
Adapter::JSON.from json
|
10
|
+
end
|
11
|
+
|
12
|
+
class Adapter
|
13
|
+
class JSON
|
14
|
+
# 定义类方法
|
15
|
+
class << self
|
16
|
+
require "json"
|
17
|
+
# 将 RUBY(HASH) 数据结构转换为 JSON
|
18
|
+
def to(hash)
|
19
|
+
::JSON.pretty_generate hash
|
20
|
+
end
|
21
|
+
# 将 JSON 转换为 RUBY 数据结构
|
22
|
+
def from(json)
|
23
|
+
::JSON.load json
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Strada
|
4
|
+
def to_toml(config)
|
5
|
+
Adapter::TOML.to config._config_to_hash
|
6
|
+
end
|
7
|
+
|
8
|
+
def from_toml(toml)
|
9
|
+
Adapter::TOML.from toml
|
10
|
+
end
|
11
|
+
|
12
|
+
class Adapter
|
13
|
+
class TOML
|
14
|
+
# 定义类方法
|
15
|
+
class << self
|
16
|
+
require "toml"
|
17
|
+
|
18
|
+
# 将 RUBY(HASH) 数据结构转换为 TOML
|
19
|
+
def to(hash)
|
20
|
+
::TOML::Generator.new(hash).body
|
21
|
+
end
|
22
|
+
# 将 TOML 转换为 RUBY 数据结构
|
23
|
+
def from(toml)
|
24
|
+
::TOML.load toml
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Strada
|
4
|
+
def to_yaml(config)
|
5
|
+
Adapter::YAML.to config._config_to_hash
|
6
|
+
end
|
7
|
+
|
8
|
+
def from_yaml(yaml)
|
9
|
+
Adapter::YAML.from yaml
|
10
|
+
end
|
11
|
+
|
12
|
+
class Adapter
|
13
|
+
class YAML
|
14
|
+
# 定义类方法
|
15
|
+
class << self
|
16
|
+
require "yaml"
|
17
|
+
|
18
|
+
# 将 RUBY(HASH) 数据结构转换为 YAML
|
19
|
+
def to(hash)
|
20
|
+
::YAML.dump hash
|
21
|
+
end
|
22
|
+
# 将 YAML 转换为 RUBY(HASH) 数据结构
|
23
|
+
def from(yaml)
|
24
|
+
::YAML.load yaml
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Strada
|
4
|
+
class ConfigStruct
|
5
|
+
# 将配置信息转换为 HASH 对象
|
6
|
+
def _config_to_hash
|
7
|
+
hash = {}
|
8
|
+
@cfg.each do |key, value|
|
9
|
+
if value.class == ConfigStruct
|
10
|
+
value = value._config_to_hash
|
11
|
+
end
|
12
|
+
key = key.to_s if @key_to_s
|
13
|
+
hash[key] = value
|
14
|
+
end
|
15
|
+
hash
|
16
|
+
end
|
17
|
+
|
18
|
+
# 是否为空配置
|
19
|
+
def empty?
|
20
|
+
@cfg.empty?
|
21
|
+
end
|
22
|
+
|
23
|
+
# 调用配置 each 方法执行代码块
|
24
|
+
def each(&block)
|
25
|
+
@cfg.each(&block)
|
26
|
+
end
|
27
|
+
|
28
|
+
# 配置的相关属性
|
29
|
+
def keys
|
30
|
+
@cfg.keys
|
31
|
+
end
|
32
|
+
|
33
|
+
# 判断配置是否包含某个属性
|
34
|
+
def has_key?(key)
|
35
|
+
@cfg.has_key? key
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def initialize(hash = nil, opts = {})
|
40
|
+
@key_to_s = opts.delete :key_to_s
|
41
|
+
@cfg = hash ? _config_from_hash(hash) : {}
|
42
|
+
end
|
43
|
+
|
44
|
+
# 方法反射
|
45
|
+
def method_missing(name, *args, &block)
|
46
|
+
name = name.to_s
|
47
|
+
name = args.shift if name[0..1] == "[]" # strada.cfg['foo']
|
48
|
+
arg = args.first
|
49
|
+
|
50
|
+
if name[-1..-1] == "?" # strada.cfg.foo.bar?
|
51
|
+
if @cfg.has_key? name[0..-2]
|
52
|
+
@cfg[name[0..-2]]
|
53
|
+
else
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
elsif name[-1..-1] == "=" # strada.cfg.foo.bar = 'quux'
|
57
|
+
_config_set name[0..-2], arg
|
58
|
+
else
|
59
|
+
_config_get name, arg # strada.cfg.foo.bar
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# 设置键值对
|
64
|
+
def _config_set(key, value)
|
65
|
+
@cfg[key] = value
|
66
|
+
end
|
67
|
+
|
68
|
+
# 查询 KEY VALUE
|
69
|
+
def _config_get(key, value)
|
70
|
+
if @cfg.has_key? key
|
71
|
+
@cfg[key]
|
72
|
+
else
|
73
|
+
@cfg[key] = ConfigStruct.new
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# 转换 HASH 数据为配置对象
|
78
|
+
def _config_from_hash(hash)
|
79
|
+
cfg = {}
|
80
|
+
hash.each do |key, value|
|
81
|
+
if value.class == Hash
|
82
|
+
value = ConfigStruct.new value, key_to_s: @key_to_s
|
83
|
+
end
|
84
|
+
cfg[key] = value
|
85
|
+
end
|
86
|
+
cfg
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/strada.rb
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "strada/config_struct"
|
4
|
+
require_relative "strada/adapter/yaml"
|
5
|
+
require_relative "strada/adapter/json"
|
6
|
+
require_relative "strada/adapter/toml"
|
7
|
+
require "fileutils"
|
8
|
+
|
9
|
+
class Strada < StandardError; end
|
10
|
+
|
11
|
+
class NoName < ConfigError; end
|
12
|
+
|
13
|
+
class UnknownOption < ConfigError; end
|
14
|
+
|
15
|
+
# @example common use case
|
16
|
+
# CFGS = Strada.new :name=>'my_sweet_program' :load=>false # do not load config from filesystem
|
17
|
+
# CFGS.default.ssh.port = 22
|
18
|
+
# CFGS.default.ssh.hosts = %w(host1.example.com host2.example.com)
|
19
|
+
# CFGS.default.auth.user = lana
|
20
|
+
# CFGS.default.auth.password = danger_zone
|
21
|
+
# CFGS.load # load system config and user config from filesystem and merge with defaults to #cfg
|
22
|
+
# raise StandardError, 'edit ~/.config/my_sweet_program/config' if CFGS.create # create user config from default config if no system or user config exists
|
23
|
+
# # use the damn thing
|
24
|
+
# CFG = CFGS.cfg
|
25
|
+
# user = CFG.auth.user
|
26
|
+
# password = CFG.auth.password
|
27
|
+
# ssh_port = CFG.ssh.port
|
28
|
+
# ssh_hosts = CFG.ssh.hosts
|
29
|
+
|
30
|
+
class Strada
|
31
|
+
CONFIG_FILE = "config"
|
32
|
+
# 类对象属性
|
33
|
+
attr_reader :cfg, :default, :file
|
34
|
+
attr_accessor :system, :user
|
35
|
+
|
36
|
+
# 类方法
|
37
|
+
class << self
|
38
|
+
def cfg(*args)
|
39
|
+
new(*args).cfg
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# When this is called, by default :system and :user are loaded from
|
44
|
+
# filesystem and merged with default, so that user overrides system which
|
45
|
+
# overrides default
|
46
|
+
#
|
47
|
+
# @param [Symbol] level which configuration level to load, by default :all
|
48
|
+
# @return [void]
|
49
|
+
def load(level = :all)
|
50
|
+
if (level == :default) || (level == :all)
|
51
|
+
@cfg = merge @cfg, @default
|
52
|
+
end
|
53
|
+
if (level == :system) || (level == :all)
|
54
|
+
@system = load_cfg @sys_dir
|
55
|
+
@cfg = merge @cfg, @system
|
56
|
+
end
|
57
|
+
if (level == :user) || (level == :all)
|
58
|
+
@user = load_cfg @usr_dir
|
59
|
+
@cfg = merge @cfg, @user
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# @param [Symbol] level which configuration level to save, by default :user
|
64
|
+
# @return [void]
|
65
|
+
def save(level = :user)
|
66
|
+
if level == :user
|
67
|
+
save_cfg @usr_dir, @user
|
68
|
+
elsif level == :system
|
69
|
+
save_cfg @sys_dir, @system
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# @example create user config from default config and raise error, if no config was found
|
74
|
+
# raise StandardError, 'edit ~/.config/name/config' if strada.create
|
75
|
+
# @param [Hash] opts options for Strada
|
76
|
+
# @option opts [Symbol] :source source to use for settings to save, by default :default
|
77
|
+
# @option opts [Symbol] :destination destination to use for settings to save, by default :user
|
78
|
+
# @option opts [boolean] :load load config once saved, by default false
|
79
|
+
# @return [boolean] true if config didn't exist and was created, false if config already exists
|
80
|
+
def create(opts = {})
|
81
|
+
src = opts.delete :source || :default
|
82
|
+
dst = opts.delete :destination || :user
|
83
|
+
|
84
|
+
no_config = false
|
85
|
+
no_config = true if @system.empty? && @user.empty?
|
86
|
+
if no_config
|
87
|
+
src = instance_variable_get "@" + src.to_s
|
88
|
+
instance_variable_set("@" + dst.to_s, src.dup)
|
89
|
+
save dst
|
90
|
+
load if opts.delete :load
|
91
|
+
end
|
92
|
+
no_config
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
def initialize(opts = {})
|
97
|
+
# @param [Hash] opts options for Strada.new
|
98
|
+
# @option opts [String] :name name to use for strada (/etc/name/, ~/.config/name/) - autodetected if not defined
|
99
|
+
# @option opts [String] :adapter adapter to use 'yaml', 'json' or 'toml' for now
|
100
|
+
# @option opts [String] :usr_dir directory for storing user config ~/.config/name/ by default
|
101
|
+
# @option opts [String] :sys_dir directory for storing system config /etc/name/ by default
|
102
|
+
# @option opts [String] :cfg_file configuration filename, by default CONFIG_FILE
|
103
|
+
# @option opts [Hash] :default default settings to use
|
104
|
+
# @option opts [boolean] :load automatically load+merge system+user config with defaults in #cfg
|
105
|
+
# @option opts [boolean] :key_to_s convert keys to string by calling #to_s for keys
|
106
|
+
@name = opts.delete(:name) || meta_name
|
107
|
+
@adapter = opts.delete(:adapter) || "yaml"
|
108
|
+
@usr_dir = opts.delete(:usr_dir) || File.join(Dir.home, ".config", @name)
|
109
|
+
@sys_dir = opts.delete(:sys_dir) || File.join("/etc", @name)
|
110
|
+
@cfg_file = opts.delete(:cfg_file) || CONFIG_FILE
|
111
|
+
|
112
|
+
# 配置对象属性
|
113
|
+
@default = ConfigStruct.new opts.delete(:default)
|
114
|
+
@system = ConfigStruct.new
|
115
|
+
@user = ConfigStruct.new
|
116
|
+
@cfg = ConfigStruct.new
|
117
|
+
@load = true
|
118
|
+
@load = opts.delete(:load) if opts.has_key?(:load)
|
119
|
+
@key_to_s = opts.delete(:key_to_s)
|
120
|
+
raise UnknownOption, "option '#{opts}' not recognized" unless opts.empty?
|
121
|
+
load :all if @load
|
122
|
+
end
|
123
|
+
|
124
|
+
# 加载配置文件
|
125
|
+
def load_cfg(dir)
|
126
|
+
@file = File.join dir, @cfg_file
|
127
|
+
file = File.read @file
|
128
|
+
ConfigStruct.new(from(@adapter, file), key_to_s: @key_to_s)
|
129
|
+
rescue Errno::ENOENT
|
130
|
+
ConfigStruct.new
|
131
|
+
end
|
132
|
+
|
133
|
+
# 保存配置文件
|
134
|
+
def save_cfg(dir, config)
|
135
|
+
config = to(@adapter, config)
|
136
|
+
file = File.join dir, @cfg_file
|
137
|
+
FileUtils.mkdir_p dir
|
138
|
+
File.write file, config
|
139
|
+
end
|
140
|
+
|
141
|
+
# 合并配置属性
|
142
|
+
def merge(*configs)
|
143
|
+
hash = {}
|
144
|
+
# 将相关配置依次迭代并转换为 RUBY HASH 对象
|
145
|
+
configs.each do |config|
|
146
|
+
hash = hash._config_deep_merge config._config_to_hash
|
147
|
+
end
|
148
|
+
# 将合并后的 HASH 数据结构转换为配置对象
|
149
|
+
ConfigStruct.new hash
|
150
|
+
end
|
151
|
+
|
152
|
+
# 将 JSON|YAML|TOML 等数据格式转换为 RUBY 数据结构
|
153
|
+
def from(adapter, string)
|
154
|
+
name = "from_" + adapter
|
155
|
+
send name, string
|
156
|
+
end
|
157
|
+
|
158
|
+
# 将 RUBY 数据结构转换为 JSON|YAML|TOML 对象
|
159
|
+
def to(adapter, config)
|
160
|
+
name = "to_" + adapter
|
161
|
+
send name, config
|
162
|
+
end
|
163
|
+
|
164
|
+
# 基础的配置标致名称
|
165
|
+
def meta_name
|
166
|
+
path = caller_locations[-1].path
|
167
|
+
File.basename path, File.extname(path)
|
168
|
+
rescue
|
169
|
+
raise NoName, "can't figure out name, specify explicitly"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# 增加 HASH 方法
|
174
|
+
class Hash
|
175
|
+
def _config_deep_merge(new_hash)
|
176
|
+
merger = proc do |key, old_val, new_val|
|
177
|
+
Hash === old_val && Hash === new_val ? old_val.merge(new_val, &merger) : new_val
|
178
|
+
end
|
179
|
+
merge new_hash, &merger
|
180
|
+
end
|
181
|
+
end
|
data/strada.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/strada/version"
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "strada"
|
6
|
+
s.version = Strada::VERSION
|
7
|
+
s.licenses = ["MIT"]
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["WENWU.YAN"]
|
10
|
+
s.email = %w( 968826@gmail.com )
|
11
|
+
s.homepage = "http://github.com/ciscolive/strada"
|
12
|
+
s.summary = "configuration library"
|
13
|
+
s.description = "configuration library with object access to YAML/JSON/TOML backends"
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.executables = %w( )
|
16
|
+
s.require_path = "lib"
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: strada
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- WENWU.YAN
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-04-14 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: configuration library with object access to YAML/JSON/TOML backends
|
14
|
+
email:
|
15
|
+
- 968826@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- ".gitignore"
|
21
|
+
- ".rubocop.yml"
|
22
|
+
- Gemfile
|
23
|
+
- README.md
|
24
|
+
- Rakefile
|
25
|
+
- lib/strada.rb
|
26
|
+
- lib/strada/adapter/json.rb
|
27
|
+
- lib/strada/adapter/toml.rb
|
28
|
+
- lib/strada/adapter/yaml.rb
|
29
|
+
- lib/strada/config_struct.rb
|
30
|
+
- lib/strada/version.rb
|
31
|
+
- strada.gemspec
|
32
|
+
homepage: http://github.com/ciscolive/strada
|
33
|
+
licenses:
|
34
|
+
- MIT
|
35
|
+
metadata: {}
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options: []
|
38
|
+
require_paths:
|
39
|
+
- lib
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
requirements: []
|
51
|
+
rubygems_version: 3.3.3
|
52
|
+
signing_key:
|
53
|
+
specification_version: 4
|
54
|
+
summary: configuration library
|
55
|
+
test_files: []
|