jac 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/jac/configuration.rb +97 -41
- data/lib/jac/version.rb +3 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8aaaff9c5e46908f214469f894732bdbd178bdc92e91549180d7f3a45157e2e2
|
4
|
+
data.tar.gz: d6c97e79b6a72e363777af65b5d9e18fb7a2545dd56b3a523ed25d9c1cf33482
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2244d961c0c82fb75ae3dffc5ce7f42763533f0bc2794062ab2a0766ade890eca579aa7dbf49f28bb2759014b3daaf2f4fa077a5a1d1dde730b8e73226ae635
|
7
|
+
data.tar.gz: 6118f96a703d7a4049b404f6635bf78c878665a2f292cc7c5fc3b6812aa14ec9f2ba62c14118b6cf2f183c31466ea2a6510fda92efdae529586aff56e3c25f30
|
data/lib/jac/configuration.rb
CHANGED
@@ -177,14 +177,28 @@ module Jac
|
|
177
177
|
# If profile name matches multiple generic profiles it not defined
|
178
178
|
# which profile will be used.
|
179
179
|
module Configuration
|
180
|
+
# Name of top level profile. ^top profile is implicit
|
181
|
+
# it automaticly merged on top of resulting configuration
|
182
|
+
TOP_PROFILE_NAME = '^top'.freeze
|
183
|
+
# Name of base level profile. ^base profile is implicit
|
184
|
+
# resulting configuration automaticly merged on top of it.
|
185
|
+
BASE_PROFILE_NAME = '^base'.freeze
|
186
|
+
# Any configuration set always contains `default` profile
|
187
|
+
# which is loaded when no profile requested.
|
188
|
+
DEFAULT_PROFILE_NAME = 'default'.freeze
|
189
|
+
# List of default profiles. Having this list we can easily create default
|
190
|
+
# configuration.
|
191
|
+
BASIC_PROFILES = [
|
192
|
+
TOP_PROFILE_NAME,
|
193
|
+
BASE_PROFILE_NAME,
|
194
|
+
DEFAULT_PROFILE_NAME
|
195
|
+
].freeze
|
196
|
+
# Key where all inherited profiles listed
|
197
|
+
EXTENDS_KEY = 'extends'.freeze
|
198
|
+
|
180
199
|
# Reads and evaluates configuration for given set of streams
|
181
200
|
# and profile
|
182
201
|
class ConfigurationReader
|
183
|
-
# Any configuration set always contains `default` profile
|
184
|
-
# which is loaded when no profile requested.
|
185
|
-
DEFAULT_PROFILE_NAME = 'default'.freeze
|
186
|
-
# Creates "empty" config
|
187
|
-
DEFAULT_CONFIGURATION = -> () { { DEFAULT_PROFILE_NAME => {} } }
|
188
202
|
attr_reader :merger
|
189
203
|
# Creates configuration reader
|
190
204
|
# @param [Array] streams of pairs containing YAML document and provided
|
@@ -200,12 +214,22 @@ module Jac
|
|
200
214
|
def read(*profile)
|
201
215
|
result = @streams
|
202
216
|
.flat_map { |stream, _name| read_stream(stream) }
|
203
|
-
.inject(
|
204
|
-
|
217
|
+
.inject(default_configuration) { |acc, elem| update(acc, elem) }
|
218
|
+
# Keep original profile name
|
219
|
+
original_profile = profile
|
220
|
+
# Add implicit profiles
|
221
|
+
profile =
|
222
|
+
[Configuration::BASE_PROFILE_NAME, profile, Configuration::TOP_PROFILE_NAME].flatten
|
223
|
+
OpenStruct.new(evaluate(resolve(profile, result).merge('profile' => original_profile)))
|
205
224
|
end
|
206
225
|
|
207
226
|
private
|
208
227
|
|
228
|
+
# Creates empty configuration object.
|
229
|
+
def default_configuration
|
230
|
+
Configuration::BASIC_PROFILES.inject({}) { |acc, elem| acc.update(elem => {}) }
|
231
|
+
end
|
232
|
+
|
209
233
|
def read_stream(stream)
|
210
234
|
# Each stream consists of one or more documents
|
211
235
|
YAML.parse_stream(stream).children.flat_map do |document|
|
@@ -256,9 +280,6 @@ module Jac
|
|
256
280
|
|
257
281
|
# Describes profile resolving strategy
|
258
282
|
class ProfileResolver
|
259
|
-
# Key where all inherited profiles listed
|
260
|
-
EXTENDS_KEY = 'extends'.freeze
|
261
|
-
|
262
283
|
attr_reader :config, :merger
|
263
284
|
|
264
285
|
def initialize(config)
|
@@ -273,8 +294,8 @@ module Jac
|
|
273
294
|
raise(ArgumentError, msg)
|
274
295
|
end
|
275
296
|
profile_values = find_profile(elem)
|
276
|
-
# Find all
|
277
|
-
extends =
|
297
|
+
# Find all parent_profiles
|
298
|
+
extends = parent_profiles(profile_values)
|
278
299
|
# We can do not check extends. Empty profile returns {}
|
279
300
|
# Inherited values goes first
|
280
301
|
inherited = merger.merge(acc, resolve(extends, resolved + [elem]))
|
@@ -282,6 +303,17 @@ module Jac
|
|
282
303
|
end
|
283
304
|
end
|
284
305
|
|
306
|
+
# Fetches list of parent profile
|
307
|
+
# @param [Hash] values current profile values
|
308
|
+
# @return [Array] of profile names
|
309
|
+
def parent_profiles(values)
|
310
|
+
extends = *values[Configuration::EXTENDS_KEY] || []
|
311
|
+
[Configuration::TOP_PROFILE_NAME, Configuration::BASE_PROFILE_NAME].each do |implicit|
|
312
|
+
raise(ArgumentError, "`#{implicit}` is not allowed here") if extends.include?(implicit)
|
313
|
+
end
|
314
|
+
extends
|
315
|
+
end
|
316
|
+
|
285
317
|
def find_profile(profile_name)
|
286
318
|
return config[profile_name] if config.key?(profile_name)
|
287
319
|
# First and last chars are '/'
|
@@ -317,8 +349,6 @@ module Jac
|
|
317
349
|
.keys
|
318
350
|
.select { |k| k[0] == '/' && k[-1] == '/' }
|
319
351
|
.map { |k| [k, Regexp.new(k[1..-2])] }
|
320
|
-
|
321
|
-
@generic_profiles
|
322
352
|
end
|
323
353
|
end
|
324
354
|
|
@@ -326,29 +356,27 @@ module Jac
|
|
326
356
|
# when referencing profile inside evaluated
|
327
357
|
# expressions
|
328
358
|
class EvaluationContext
|
329
|
-
def initialize(evaluator)
|
359
|
+
def initialize(hash, evaluator)
|
330
360
|
@evaluator = evaluator
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
return super unless respond_to_missing?(meth, args, &block)
|
340
|
-
@evaluator.evaluate(meth.to_s)
|
361
|
+
hash.each_key do |key|
|
362
|
+
exp = <<-EVAL.strip_indent
|
363
|
+
def #{key}
|
364
|
+
@evaluator.evaluate("#{key}")
|
365
|
+
end
|
366
|
+
EVAL
|
367
|
+
eval(exp, binding)
|
368
|
+
end
|
341
369
|
end
|
342
370
|
end
|
343
371
|
|
344
372
|
# Evaluates all strings inside resolved profile
|
345
373
|
# object
|
346
374
|
class ConfigurationEvaluator
|
347
|
-
def initialize(src_object
|
375
|
+
def initialize(src_object)
|
348
376
|
@object = src_object
|
349
|
-
@evaluated =
|
350
|
-
|
351
|
-
|
377
|
+
@evaluated = {}
|
378
|
+
# Initialize context object with root
|
379
|
+
@context = EvaluationContext.new(@object, self)
|
352
380
|
end
|
353
381
|
|
354
382
|
def evaluate(key)
|
@@ -364,27 +392,32 @@ module Jac
|
|
364
392
|
alias conf c
|
365
393
|
alias cfg c
|
366
394
|
|
367
|
-
|
368
|
-
|
369
|
-
def resolve_object
|
395
|
+
def evaluate_values
|
396
|
+
@ctx_object = @context
|
370
397
|
@object.each_key { |k| evaluate(k) }
|
371
398
|
# Cleanup accidentally created values (when referencing missing values)
|
372
399
|
@evaluated.delete_if { |k, _v| !@object.key?(k) }
|
373
400
|
end
|
374
401
|
|
375
|
-
|
402
|
+
private
|
403
|
+
|
404
|
+
def get_binding(object)
|
376
405
|
binding
|
377
406
|
end
|
378
407
|
|
379
|
-
def evaluate_deep(object)
|
408
|
+
def evaluate_deep(object) # rubocop:disable Metrics/MethodLength
|
380
409
|
case object
|
381
410
|
when String
|
382
411
|
eval_string(object)
|
383
412
|
when Array
|
384
|
-
object
|
413
|
+
contextual(object) do
|
414
|
+
object.map { |e| evaluate_deep(e) }
|
415
|
+
end
|
385
416
|
when Hash
|
386
417
|
# Evaluating values only by convention
|
387
|
-
object
|
418
|
+
contextual(object) do
|
419
|
+
object.inject({}) { |acc, elem| acc.update(elem.first => evaluate_deep(elem.last)) }
|
420
|
+
end
|
388
421
|
else
|
389
422
|
object
|
390
423
|
end
|
@@ -392,17 +425,40 @@ module Jac
|
|
392
425
|
|
393
426
|
def eval_string(o)
|
394
427
|
evaluated = /#\{.+?\}/.match(o) do
|
395
|
-
eval('"' + o + '"', get_binding(
|
428
|
+
eval('"' + o + '"', get_binding(@ctx_object))
|
396
429
|
end
|
397
430
|
|
398
431
|
evaluated || o
|
399
432
|
end
|
400
433
|
|
434
|
+
def respond_to_missing?(meth, args, &block)
|
435
|
+
@ctx_object && @ctx_object.respond_to?(meth, args, &block)
|
436
|
+
end
|
437
|
+
|
438
|
+
def method_missing(meth, *args, &block)
|
439
|
+
# rubocop inspection hack
|
440
|
+
return super unless respond_to_missing?(meth, args, &block)
|
441
|
+
@ctx_object.send(meth, *args, &block)
|
442
|
+
end
|
443
|
+
|
444
|
+
# Keeps track of current context objects
|
445
|
+
# This trick allows us to reference local variables
|
446
|
+
# using `self` calls.
|
447
|
+
# @param ctx [Object] object to use in current evaluation
|
448
|
+
# @param &block [Proc] evaluation logic
|
449
|
+
# @return [Object] result of evaluation
|
450
|
+
def contextual(ctx)
|
451
|
+
memo = @ctx_object
|
452
|
+
@ctx_object = ctx
|
453
|
+
result = yield
|
454
|
+
@ctx_object = memo
|
455
|
+
|
456
|
+
result
|
457
|
+
end
|
458
|
+
|
401
459
|
class << self
|
402
460
|
def evaluate(o)
|
403
|
-
|
404
|
-
ConfigurationEvaluator.new(o, dst)
|
405
|
-
dst
|
461
|
+
ConfigurationEvaluator.new(o).evaluate_values
|
406
462
|
end
|
407
463
|
end
|
408
464
|
end
|
@@ -424,7 +480,7 @@ module Jac
|
|
424
480
|
# names to read
|
425
481
|
# @return [OpenStruct] instance which contains all resolved profile fields
|
426
482
|
def read(profile, *streams)
|
427
|
-
profile = [
|
483
|
+
profile = [Configuration::DEFAULT_PROFILE_NAME] if profile.empty?
|
428
484
|
ConfigurationReader.new(streams).read(*profile)
|
429
485
|
end
|
430
486
|
|
data/lib/jac/version.rb
ADDED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jac
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ilya.arkhanhelsky
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-08-06 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Profile based configuration lib
|
14
14
|
email: ilya.arkhanhelsky@vizor-games.com
|
@@ -20,6 +20,7 @@ files:
|
|
20
20
|
- lib/jac/configuration.rb
|
21
21
|
- lib/jac/merger.rb
|
22
22
|
- lib/jac/parser.rb
|
23
|
+
- lib/jac/version.rb
|
23
24
|
homepage: https://github.com/vizor-games/jac
|
24
25
|
licenses:
|
25
26
|
- MIT
|
@@ -40,7 +41,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
40
41
|
version: '0'
|
41
42
|
requirements: []
|
42
43
|
rubyforge_project:
|
43
|
-
rubygems_version: 2.6
|
44
|
+
rubygems_version: 2.7.6
|
44
45
|
signing_key:
|
45
46
|
specification_version: 4
|
46
47
|
summary: Just Another Configuration Lib
|