strada 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ 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: []