jac 0.0.3 → 0.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 8f6cffab29062b76d447fff5f941bebe410deb1f
4
- data.tar.gz: b940de7ad2090b3c32b6c8012acfc9f1ef4fe764
2
+ SHA256:
3
+ metadata.gz: 8aaaff9c5e46908f214469f894732bdbd178bdc92e91549180d7f3a45157e2e2
4
+ data.tar.gz: d6c97e79b6a72e363777af65b5d9e18fb7a2545dd56b3a523ed25d9c1cf33482
5
5
  SHA512:
6
- metadata.gz: 87be3a20069dadd3e4cb4118ffe24cb6eaa3c25eb574e12bc14e3631132b505199f3bc74efd3eba1bb8cefd8e91d0f37e8c098a3749905f696e6281195b56407
7
- data.tar.gz: 9360da4e099d453bfa85f1a8b6f3a65252403fa5e48a36b01e89bfd69ef4715874c93fd5d138059374612908d38e8b9c36ae92271b8b80dd8072393e320f3a62
6
+ metadata.gz: b2244d961c0c82fb75ae3dffc5ce7f42763533f0bc2794062ab2a0766ade890eca579aa7dbf49f28bb2759014b3daaf2f4fa077a5a1d1dde730b8e73226ae635
7
+ data.tar.gz: 6118f96a703d7a4049b404f6635bf78c878665a2f292cc7c5fc3b6812aa14ec9f2ba62c14118b6cf2f183c31466ea2a6510fda92efdae529586aff56e3c25f30
@@ -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(DEFAULT_CONFIGURATION.call) { |acc, elem| update(acc, elem) }
204
- OpenStruct.new(evaluate(resolve(profile, result)).merge('profile' => profile))
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 inheritors
277
- extends = *profile_values[EXTENDS_KEY] || []
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
- end
332
-
333
- def respond_to_missing?(_meth, _args, &_block)
334
- true
335
- end
336
-
337
- def method_missing(meth, *args, &block)
338
- # rubocop ispection hack
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, dst_object)
375
+ def initialize(src_object)
348
376
  @object = src_object
349
- @evaluated = dst_object
350
- @context = EvaluationContext.new(self)
351
- resolve_object
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
- private
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
- def get_binding(obj)
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.map { |e| evaluate_deep(e) }
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.inject({}) { |acc, elem| acc.update(elem.first => evaluate_deep(elem.last)) }
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(self))
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
- dst = {}
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 = [ConfigurationReader::DEFAULT_PROFILE_NAME] if profile.empty?
483
+ profile = [Configuration::DEFAULT_PROFILE_NAME] if profile.empty?
428
484
  ConfigurationReader.new(streams).read(*profile)
429
485
  end
430
486
 
@@ -0,0 +1,3 @@
1
+ module Jac
2
+ VERSION = [0, 0, 4].freeze
3
+ end
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.3
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: 2017-11-15 00:00:00.000000000 Z
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.13
44
+ rubygems_version: 2.7.6
44
45
  signing_key:
45
46
  specification_version: 4
46
47
  summary: Just Another Configuration Lib