strada 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ .*gems
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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Strada
4
+ VERSION = "0.0.1"
5
+ 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: []