Module: Hocon::Impl::AbstractConfigValue

Overview

Trying very hard to avoid a parent reference in config values; when you have a tree like this, the availability of parent() tends to result in a lot of improperly-factored and non-modular code. Please don’t add parent().

Defined Under Namespace

Modules: NoExceptionsModifier Classes: NotPossibleToResolve

Constant Summary collapse

ConfigImplUtil =
Hocon::Impl::ConfigImplUtil
ConfigBugOrBrokenError =
Hocon::ConfigError::ConfigBugOrBrokenError
ResolveStatus =
Hocon::Impl::ResolveStatus

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ConfigValue

#unwrapped, #value_type

Instance Attribute Details

#originObject (readonly)

Returns the value of attribute origin.



30
31
32
# File 'lib/hocon/impl/abstract_config_value.rb', line 30

def origin
  @origin
end

Class Method Details

.has_descendant_in_list?(list, descendant) ⇒ Boolean

Returns:

  • (Boolean)


89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/hocon/impl/abstract_config_value.rb', line 89

def self.has_descendant_in_list?(list, descendant)
  list.each do |v|
    if v.equal?(descendant)
      return true
    end
  end
  # now the expensive traversal
  list.each do |v|
    if v.is_a?(Hocon::Impl::Container) && v.has_descendant?(descendant)
      return true
    end
  end
  false
end

.indent(sb, indent_size, options) ⇒ Object



294
295
296
297
298
299
300
301
302
# File 'lib/hocon/impl/abstract_config_value.rb', line 294

def self.indent(sb, indent_size, options)
  if options.formatted?
    remaining = indent_size
    while remaining > 0
      sb << "    "
      remaining -= 1
    end
  end
end

.replace_child_in_list(list, child, replacement) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/hocon/impl/abstract_config_value.rb', line 66

def self.replace_child_in_list(list, child, replacement)
  i = 0
  while (i < list.size) && (! list[i].equal?(child))
    i += 1
  end
  if (i == list.size)
    raise ConfigBugOrBrokenError, "tried to replace #{child} which is not in #{list}"
  end

  new_stack = list.clone
  if ! replacement.nil?
    new_stack[i] = replacement
  else
    new_stack.delete(i)
  end

  if new_stack.empty?
    nil
  else
    new_stack
  end
end

Instance Method Details

#==(other) ⇒ Object



263
264
265
266
267
268
269
270
271
272
# File 'lib/hocon/impl/abstract_config_value.rb', line 263

def ==(other)
  # note that "origin" is deliberately NOT part of equality
  if other.is_a?(Hocon::Impl::AbstractConfigValue)
    can_equal(other) &&
        value_type == other.value_type &&
        ConfigImplUtil.equals_handling_nil?(unwrapped, other.unwrapped)
  else
    false
  end
end

#at_key(key) ⇒ Object



356
357
358
# File 'lib/hocon/impl/abstract_config_value.rb', line 356

def at_key(key)
  at_key_with_origin(Hocon::Impl::SimpleConfigOrigin.new_simple("at_key(#{key})"), key)
end

#at_key_with_origin(origin, key) ⇒ Object

Renamed this to be consistent with the other at_key* overloaded methods



361
362
363
364
# File 'lib/hocon/impl/abstract_config_value.rb', line 361

def at_key_with_origin(origin, key)
  m = {key=>self}
  Hocon::Impl::SimpleConfigObject.new(origin, m).to_config
end

#at_path(path_expression) ⇒ Object



378
379
380
381
# File 'lib/hocon/impl/abstract_config_value.rb', line 378

def at_path(path_expression)
  origin = Hocon::Impl::SimpleConfigOrigin.new_simple("at_path(#{path_expression})")
  at_path_with_origin(origin, Hocon::Impl::Path.new_path(path_expression))
end

#at_path_with_origin(origin, path) ⇒ Object

In java this is an overloaded version of atPath



367
368
369
370
371
372
373
374
375
376
# File 'lib/hocon/impl/abstract_config_value.rb', line 367

def at_path_with_origin(origin, path)
  parent = path.parent
  result = at_key_with_origin(origin, path.last)
  while not parent.nil? do
    key = parent.last
    result = result.at_key_with_origin(origin, key)
    parent = parent.parent
  end
  result
end

#can_equal(other) ⇒ Object



259
260
261
# File 'lib/hocon/impl/abstract_config_value.rb', line 259

def can_equal(other)
  other.is_a?(Hocon::Impl::AbstractConfigValue)
end

#construct_delayed_merge(origin, stack) ⇒ Object



161
162
163
164
165
166
# File 'lib/hocon/impl/abstract_config_value.rb', line 161

def construct_delayed_merge(origin, stack)
  # TODO: this might not work because ConfigDelayedMerge inherits
  # from this class, so we can't `require` it from this file
  require 'hocon/impl/config_delayed_merge'
  Hocon::Impl::ConfigDelayedMerge.new(origin, stack)
end

#delay_merge(stack, fallback) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
# File 'lib/hocon/impl/abstract_config_value.rb', line 180

def delay_merge(stack, fallback)
  # if we turn out to be an object, and the fallback also does,
  # then a merge may be required.
  # if we contain a substitution, resolving it may need to look
  # back to the fallback
  new_stack = stack.clone
  new_stack << fallback
  # TODO: this might not work because AbstractConfigObject inherits
  # from this class, so we can't `require` it from this file
  construct_delayed_merge(Hocon::Impl::AbstractConfigObject.merge_origins(new_stack), new_stack)
end

#hashObject



274
275
276
277
278
279
280
281
282
# File 'lib/hocon/impl/abstract_config_value.rb', line 274

def hash
  # note that "origin" is deliberately NOT part of equality
  unwrapped_value = unwrapped
  if unwrapped_value.nil?
    0
  else
    unwrapped_value.hash
  end
end

#ignores_fallbacks?Boolean

this is virtualized rather than a field because only some subclasses really need to store the boolean, and they may be able to pack it with another boolean to save space.

Returns:

  • (Boolean)


139
140
141
142
143
# File 'lib/hocon/impl/abstract_config_value.rb', line 139

def ignores_fallbacks?
  # if we are not resolved, then somewhere in this value there's
  # a substitution that may need to look at the fallbacks.
  resolve_status == Hocon::Impl::ResolveStatus::RESOLVED
end

#initialize(origin) ⇒ Object



26
27
28
# File 'lib/hocon/impl/abstract_config_value.rb', line 26

def initialize(origin)
  @origin = origin
end

#inspectObject



290
291
292
# File 'lib/hocon/impl/abstract_config_value.rb', line 290

def inspect
  to_s
end

#merged_stack_with_non_object(stack, fallback) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/hocon/impl/abstract_config_value.rb', line 204

def merged_stack_with_non_object(stack, fallback)
  require_not_ignoring_fallbacks

  if resolve_status == ResolveStatus::RESOLVED
    # falling back to a non-object doesn't merge anything, and also
    # prohibits merging any objects that we fall back to later.
    # so we have to switch to ignoresFallbacks mode.
    with_fallbacks_ignored
  else
    # if unresolved we may have to look back to fallbacks as part of
    # the resolution process, so always delay
    delay_merge(stack, fallback)
  end
end

#merged_stack_with_object(stack, fallback) ⇒ Object



192
193
194
195
196
197
198
199
200
201
202
# File 'lib/hocon/impl/abstract_config_value.rb', line 192

def merged_stack_with_object(stack, fallback)
  require_not_ignoring_fallbacks

  # TODO: this might not work because AbstractConfigObject inherits
  # from this class, so we can't `require` it from this file
  if self.is_a?(Hocon::Impl::AbstractConfigObject)
    raise ConfigBugOrBrokenError, "Objects must reimplement merged_with_object"
  end

  merged_stack_with_non_object(stack, fallback)
end

#merged_stack_with_the_unmergeable(stack, fallback) ⇒ Object



168
169
170
171
172
173
174
175
176
177
178
# File 'lib/hocon/impl/abstract_config_value.rb', line 168

def merged_stack_with_the_unmergeable(stack, fallback)
  require_not_ignoring_fallbacks

  # if we turn out to be an object, and the fallback also does,
  # then a merge may be required; delay until we resolve.
  new_stack = stack.clone
  new_stack.concat(fallback.unmerged_values)
  # TODO: this might not work because AbstractConfigObject inherits
  # from this class, so we can't `require` it from this file
  construct_delayed_merge(Hocon::Impl::AbstractConfigObject.merge_origins(new_stack), new_stack)
end

#merged_with_non_object(fallback) ⇒ Object



229
230
231
232
# File 'lib/hocon/impl/abstract_config_value.rb', line 229

def merged_with_non_object(fallback)
  require_not_ignoring_fallbacks
  merged_stack_with_non_object([self], fallback)
end

#merged_with_object(fallback) ⇒ Object



224
225
226
227
# File 'lib/hocon/impl/abstract_config_value.rb', line 224

def merged_with_object(fallback)
  require_not_ignoring_fallbacks
  merged_stack_with_object([self], fallback)
end

#merged_with_the_unmergeable(fallback) ⇒ Object



219
220
221
222
# File 'lib/hocon/impl/abstract_config_value.rb', line 219

def merged_with_the_unmergeable(fallback)
  require_not_ignoring_fallbacks
  merged_stack_with_the_unmergeable([self], fallback)
end

#new_copy(origin) ⇒ Object



132
133
134
# File 'lib/hocon/impl/abstract_config_value.rb', line 132

def new_copy(origin)
  raise ConfigBugOrBrokenError, "subclasses of AbstractConfigValue should provide their own implementation of `new_copy` (#{self.class})"
end

#relativized(prefix) ⇒ Object

This is used when including one file in another; the included file is relativized to the path it’s included into in the parent file. The point is that if you include a file at foo.bar in the parent, and the included file as a substitution $Hocon::Impl::AbstractConfigValue.aa.ba.b.c, the included substitution now needs to be $Hocon::Impl::AbstractConfigValue.foofoo.barfoo.bar.afoo.bar.a.bfoo.bar.a.b.c because we resolve substitutions globally only after parsing everything.

Parameters:

  • prefix

Returns:

  • value relativized to the given path or the same value if nothing to do



114
115
116
# File 'lib/hocon/impl/abstract_config_value.rb', line 114

def relativized(prefix)
  self
end

#render(options = Hocon::ConfigRenderOptions.defaults) ⇒ Object



338
339
340
341
342
343
344
345
# File 'lib/hocon/impl/abstract_config_value.rb', line 338

def render(options = Hocon::ConfigRenderOptions.defaults)
  sb = StringIO.new
  render_to_sb(sb, 0, true, nil, options)
  # We take a substring that ends at sb.pos, because we've been decrementing
  # sb.pos at various points in the code as a means to remove characters from
  # the end of the StringIO
  sb.string[0, sb.pos]
end

#render_to_sb(sb, indent, at_root, at_key, options) ⇒ Object



304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/hocon/impl/abstract_config_value.rb', line 304

def render_to_sb(sb, indent, at_root, at_key, options)
  if !at_key.nil?
    rendered_key =
        if options.json?
          ConfigImplUtil.render_json_string(at_key)
        else
          ConfigImplUtil.render_string_unquoted_if_possible(at_key)
        end

    sb << rendered_key

    if options.json?
      if options.formatted?
        sb << ": "
      else
        sb << ":"
      end
    else
      case options.key_value_separator
        when :colon
          sb << ": "
        else
          sb << "="
      end      end
  end
  render_value_to_sb(sb, indent, at_root, options)
end

#render_value_to_sb(sb, indent, at_root, options) ⇒ Object

to be overridden by subclasses



333
334
335
336
# File 'lib/hocon/impl/abstract_config_value.rb', line 333

def render_value_to_sb(sb, indent, at_root, options)
  u = unwrapped
  sb << u.to_s
end

#require_not_ignoring_fallbacksObject

the withFallback() implementation is supposed to avoid calling mergedWith* if we’re ignoring fallbacks.



155
156
157
158
159
# File 'lib/hocon/impl/abstract_config_value.rb', line 155

def require_not_ignoring_fallbacks
  if ignores_fallbacks?
    raise ConfigBugOrBrokenError, "method should not have been called with ignoresFallbacks=true #{self.class.name}"
  end
end

#resolve_statusObject



62
63
64
# File 'lib/hocon/impl/abstract_config_value.rb', line 62

def resolve_status
  Hocon::Impl::ResolveStatus::RESOLVED
end

#resolve_substitutions(context, source) ⇒ Object

Called only by ResolveContext.resolve

Parameters:

  • context

    state of the current resolve

  • source

    where to look up values

Returns:

  • a new value if there were changes, or this if no changes



58
59
60
# File 'lib/hocon/impl/abstract_config_value.rb', line 58

def resolve_substitutions(context, source)
  Hocon::Impl::ResolveResult.make(context, self)
end

#to_fallback_valueObject



128
129
130
# File 'lib/hocon/impl/abstract_config_value.rb', line 128

def to_fallback_value
  self
end

#to_sObject



284
285
286
287
288
# File 'lib/hocon/impl/abstract_config_value.rb', line 284

def to_s
  sb = StringIO.new
  render_to_sb(sb, 0, true, nil, Hocon::ConfigRenderOptions.concise)
  "#{self.class.name.split('::').last}(#{sb.string})"
end

#transform_to_stringObject

toString() is a debugging-oriented string but this is defined to create a string that would parse back to the value in JSON. It only works for primitive values (that would be a single which are auto-converted to strings when concatenating with other strings or by the DefaultTransformer.



352
353
354
# File 'lib/hocon/impl/abstract_config_value.rb', line 352

def transform_to_string
  nil
end

#with_fallback(mergeable) ⇒ Object



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/hocon/impl/abstract_config_value.rb', line 242

def with_fallback(mergeable)
  if ignores_fallbacks?
    self
  else
    other = mergeable.to_fallback_value
    if other.is_a?(Hocon::Impl::Unmergeable)
      merged_with_the_unmergeable(other)
      # TODO: this probably isn't going to work because AbstractConfigObject inherits
      # from this class, so we can't `require` it from this file
    elsif other.is_a?(Hocon::Impl::AbstractConfigObject)
      merged_with_object(other)
    else
      merged_with_non_object(other)
    end
  end
end

#with_fallbacks_ignoredObject



145
146
147
148
149
150
151
# File 'lib/hocon/impl/abstract_config_value.rb', line 145

def with_fallbacks_ignored
  if ignores_fallbacks?
    self
  else
    raise ConfigBugOrBrokenError, "value class doesn't implement forced fallback-ignoring #{self}"
  end
end

#with_origin(origin) ⇒ Object



234
235
236
237
238
239
240
# File 'lib/hocon/impl/abstract_config_value.rb', line 234

def with_origin(origin)
  if @origin.equal?(origin)
    self
  else
    new_copy(origin)
  end
end