hocon 0.0.7 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +4 -2
  3. data/lib/hocon.rb +2 -0
  4. data/lib/hocon/config.rb +1010 -0
  5. data/lib/hocon/config_error.rb +32 -2
  6. data/lib/hocon/config_factory.rb +46 -0
  7. data/lib/hocon/config_include_context.rb +49 -0
  8. data/lib/hocon/config_includer_file.rb +27 -0
  9. data/lib/hocon/config_list.rb +49 -0
  10. data/lib/hocon/config_mergeable.rb +74 -0
  11. data/lib/hocon/config_object.rb +144 -1
  12. data/lib/hocon/config_parse_options.rb +33 -9
  13. data/lib/hocon/config_parseable.rb +51 -0
  14. data/lib/hocon/config_render_options.rb +4 -2
  15. data/lib/hocon/config_resolve_options.rb +31 -0
  16. data/lib/hocon/config_syntax.rb +5 -2
  17. data/lib/hocon/config_util.rb +73 -0
  18. data/lib/hocon/config_value.rb +122 -0
  19. data/lib/hocon/config_value_factory.rb +66 -2
  20. data/lib/hocon/config_value_type.rb +5 -2
  21. data/lib/hocon/impl.rb +2 -0
  22. data/lib/hocon/impl/abstract_config_node.rb +29 -0
  23. data/lib/hocon/impl/abstract_config_node_value.rb +11 -0
  24. data/lib/hocon/impl/abstract_config_object.rb +148 -42
  25. data/lib/hocon/impl/abstract_config_value.rb +251 -11
  26. data/lib/hocon/impl/array_iterator.rb +19 -0
  27. data/lib/hocon/impl/config_boolean.rb +7 -1
  28. data/lib/hocon/impl/config_concatenation.rb +177 -28
  29. data/lib/hocon/impl/config_delayed_merge.rb +329 -0
  30. data/lib/hocon/impl/config_delayed_merge_object.rb +274 -0
  31. data/lib/hocon/impl/config_document_parser.rb +647 -0
  32. data/lib/hocon/impl/config_double.rb +44 -0
  33. data/lib/hocon/impl/config_impl.rb +143 -19
  34. data/lib/hocon/impl/config_impl_util.rb +18 -0
  35. data/lib/hocon/impl/config_include_kind.rb +10 -0
  36. data/lib/hocon/impl/config_int.rb +13 -1
  37. data/lib/hocon/impl/config_node_array.rb +11 -0
  38. data/lib/hocon/impl/config_node_comment.rb +19 -0
  39. data/lib/hocon/impl/config_node_complex_value.rb +54 -0
  40. data/lib/hocon/impl/config_node_concatenation.rb +11 -0
  41. data/lib/hocon/impl/config_node_field.rb +81 -0
  42. data/lib/hocon/impl/config_node_include.rb +33 -0
  43. data/lib/hocon/impl/config_node_object.rb +276 -0
  44. data/lib/hocon/impl/config_node_path.rb +48 -0
  45. data/lib/hocon/impl/config_node_root.rb +60 -0
  46. data/lib/hocon/impl/config_node_simple_value.rb +42 -0
  47. data/lib/hocon/impl/config_node_single_token.rb +17 -0
  48. data/lib/hocon/impl/config_null.rb +15 -7
  49. data/lib/hocon/impl/config_number.rb +43 -4
  50. data/lib/hocon/impl/config_parser.rb +403 -0
  51. data/lib/hocon/impl/config_reference.rb +142 -0
  52. data/lib/hocon/impl/config_string.rb +55 -7
  53. data/lib/hocon/impl/container.rb +29 -0
  54. data/lib/hocon/impl/default_transformer.rb +24 -15
  55. data/lib/hocon/impl/from_map_mode.rb +3 -1
  56. data/lib/hocon/impl/full_includer.rb +2 -0
  57. data/lib/hocon/impl/memo_key.rb +42 -0
  58. data/lib/hocon/impl/mergeable_value.rb +8 -0
  59. data/lib/hocon/impl/origin_type.rb +8 -2
  60. data/lib/hocon/impl/parseable.rb +455 -91
  61. data/lib/hocon/impl/path.rb +181 -59
  62. data/lib/hocon/impl/path_builder.rb +24 -3
  63. data/lib/hocon/impl/path_parser.rb +280 -0
  64. data/lib/hocon/impl/replaceable_merge_stack.rb +22 -0
  65. data/lib/hocon/impl/resolve_context.rb +254 -0
  66. data/lib/hocon/impl/resolve_memos.rb +21 -0
  67. data/lib/hocon/impl/resolve_result.rb +39 -0
  68. data/lib/hocon/impl/resolve_source.rb +354 -0
  69. data/lib/hocon/impl/resolve_status.rb +3 -1
  70. data/lib/hocon/impl/simple_config.rb +264 -10
  71. data/lib/hocon/impl/simple_config_document.rb +48 -0
  72. data/lib/hocon/impl/simple_config_list.rb +282 -8
  73. data/lib/hocon/impl/simple_config_object.rb +424 -88
  74. data/lib/hocon/impl/simple_config_origin.rb +263 -71
  75. data/lib/hocon/impl/simple_include_context.rb +31 -1
  76. data/lib/hocon/impl/simple_includer.rb +196 -1
  77. data/lib/hocon/impl/substitution_expression.rb +38 -0
  78. data/lib/hocon/impl/token.rb +17 -4
  79. data/lib/hocon/impl/token_type.rb +6 -2
  80. data/lib/hocon/impl/tokenizer.rb +339 -109
  81. data/lib/hocon/impl/tokens.rb +330 -79
  82. data/lib/hocon/impl/unmergeable.rb +14 -1
  83. data/lib/hocon/impl/unsupported_operation_error.rb +6 -0
  84. data/lib/hocon/impl/url.rb +37 -0
  85. data/lib/hocon/parser.rb +7 -0
  86. data/lib/hocon/parser/config_document.rb +92 -0
  87. data/lib/hocon/parser/config_document_factory.rb +36 -0
  88. data/lib/hocon/parser/config_node.rb +30 -0
  89. metadata +67 -43
  90. data/lib/hocon/impl/config_float.rb +0 -13
  91. data/lib/hocon/impl/parser.rb +0 -977
  92. data/lib/hocon/impl/properties_parser.rb +0 -83
@@ -1,4 +1,7 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'hocon'
4
+ require 'hocon/config_error'
2
5
 
3
6
  #
4
7
  # The type of a configuration value (following the <a
@@ -20,7 +23,7 @@ module Hocon::ConfigValueType
20
23
  when BOOLEAN then "BOOLEAN"
21
24
  when NULL then "NULL"
22
25
  when STRING then "STRING"
23
- else raise ConfigBugError, "Unrecognized value type '#{config_value_type}'"
26
+ else raise Hocon::ConfigError::ConfigBugOrBrokenError, "Unrecognized value type '#{config_value_type}'"
24
27
  end
25
28
  end
26
- end
29
+ end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'hocon'
2
4
 
3
5
  module Hocon::Impl
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+
3
+ require 'hocon/impl'
4
+ require 'hocon/parser/config_node'
5
+ require 'hocon/config_error'
6
+
7
+ module Hocon::Impl::AbstractConfigNode
8
+ include Hocon::Parser::ConfigNode
9
+ def tokens
10
+ raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of AbstractConfigNode should override `tokens` (#{self.class})"
11
+ end
12
+
13
+ def render
14
+ orig_text = StringIO.new
15
+ tokens.each do |t|
16
+ orig_text << t.token_text
17
+ end
18
+ orig_text.string
19
+ end
20
+
21
+ def ==(other)
22
+ other.is_a?(Hocon::Impl::AbstractConfigNode) &&
23
+ (render == other.render)
24
+ end
25
+
26
+ def hash
27
+ render.hash
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+
3
+ require 'hocon/impl'
4
+ require 'hocon/impl/abstract_config_node'
5
+
6
+ # This essentially exists in the upstream so we can ensure only certain types of
7
+ # config nodes can be passed into some methods. That's not a problem in Ruby, so this is
8
+ # unnecessary, but it seems best to keep it around for consistency
9
+ module Hocon::Impl::AbstractConfigNodeValue
10
+ include Hocon::Impl::AbstractConfigNode
11
+ end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'hocon/impl'
2
4
  require 'hocon/impl/abstract_config_value'
3
5
  require 'hocon/impl/simple_config'
@@ -7,9 +9,13 @@ require 'hocon/impl/resolve_status'
7
9
  require 'hocon/impl/simple_config_origin'
8
10
  require 'hocon/config_error'
9
11
  require 'hocon/impl/config_impl'
12
+ require 'hocon/impl/unsupported_operation_error'
13
+ require 'hocon/impl/container'
10
14
 
11
- class Hocon::Impl::AbstractConfigObject < Hocon::Impl::AbstractConfigValue
15
+ module Hocon::Impl::AbstractConfigObject
12
16
  include Hocon::ConfigObject
17
+ include Hocon::Impl::Container
18
+ include Hocon::Impl::AbstractConfigValue
13
19
 
14
20
  ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
15
21
  ConfigNotResolvedError = Hocon::ConfigError::ConfigNotResolvedError
@@ -27,17 +33,117 @@ class Hocon::Impl::AbstractConfigObject < Hocon::Impl::AbstractConfigValue
27
33
  self
28
34
  end
29
35
 
36
+ def with_only_key(key)
37
+ raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `with_only_key`"
38
+ end
39
+
40
+ def without_key(key)
41
+ raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `without_key`"
42
+ end
43
+
44
+ def with_value(key, value)
45
+ raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `with_value`"
46
+ end
47
+
48
+ def with_only_path_or_nil(path)
49
+ raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `with_only_path_or_nil`"
50
+ end
51
+
52
+ def with_only_path(path)
53
+ raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `with_only_path`"
54
+ end
55
+
56
+ def without_path(path)
57
+ raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `without_path`"
58
+ end
59
+
60
+ def with_path_value(path, value)
61
+ raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `with_path_value`"
62
+ end
63
+
64
+ # This looks up the key with no transformation or type conversion of any
65
+ # kind, and returns null if the key is not present. The object must be
66
+ # resolved along the nodes needed to get the key or
67
+ # ConfigNotResolvedError will be thrown.
68
+ #
69
+ # @param key
70
+ # @return the unmodified raw value or null
71
+ def peek_assuming_resolved(key, original_path)
72
+ begin
73
+ attempt_peek_with_partial_resolve(key)
74
+ rescue ConfigNotResolvedError => e
75
+ raise Hocon::Impl::ConfigImpl.improve_not_resolved(original_path, e)
76
+ end
77
+ end
78
+
79
+ # Look up the key on an only-partially-resolved object, with no
80
+ # transformation or type conversion of any kind; if 'this' is not resolved
81
+ # then try to look up the key anyway if possible.
82
+ #
83
+ # @param key
84
+ # key to look up
85
+ # @return the value of the key, or null if known not to exist
86
+ # @throws ConfigNotResolvedError
87
+ # if can't figure out key's value (or existence) without more
88
+ # resolving
89
+ def attempt_peek_with_partial_resolve(key)
90
+ raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `attempt_peek_with_partial_resolve`"
91
+ end
92
+
93
+ # Looks up the path with no transformation or type conversion. Returns null
94
+ # if the path is not found; throws ConfigException.NotResolved if we need
95
+ # to go through an unresolved node to look up the path.
96
+ def peek_path(path)
97
+ peek_path_from_obj(self, path)
98
+ end
99
+
100
+ def peek_path_from_obj(obj, path)
101
+ begin
102
+ # we'll fail if anything along the path can't be looked at without resolving
103
+ path_next = path.remainder
104
+ v = obj.attempt_peek_with_partial_resolve(path.first)
105
+
106
+ if path_next.nil?
107
+ v
108
+ else
109
+ if v.is_a?(Hocon::Impl::AbstractConfigObject)
110
+ peek_path_from_obj(v, path_next)
111
+ else
112
+ nil
113
+ end
114
+ end
115
+ rescue ConfigNotResolvedError => e
116
+ raise Hocon::Impl::ConfigImpl.improve_not_resolved(path, e)
117
+ end
118
+ end
119
+
30
120
  def value_type
31
121
  Hocon::ConfigValueType::OBJECT
32
122
  end
33
123
 
124
+ def new_copy_with_status(status, origin)
125
+ raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `new_copy_with_status`"
126
+ end
127
+
34
128
  def new_copy(origin)
35
129
  new_copy_with_status(resolve_status, origin)
36
130
  end
37
131
 
38
- def merge_origins(stack)
132
+ def construct_delayed_merge(origin, stack)
133
+ Hocon::Impl::ConfigDelayedMergeObject.new(origin, stack)
134
+ end
135
+
136
+ def merged_with_object(fallback)
137
+ raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `merged_with_object`"
138
+ end
139
+
140
+ def with_fallback(mergeable)
141
+ super(mergeable)
142
+ end
143
+
144
+ def self.merge_origins(stack)
39
145
  if stack.empty?
40
- raise ConfigBugError, "can't merge origins on empty list"
146
+ raise ConfigBugOrBrokenError, "can't merge origins on empty list"
41
147
  end
42
148
  origins = []
43
149
  first_origin = nil
@@ -67,47 +173,47 @@ class Hocon::Impl::AbstractConfigObject < Hocon::Impl::AbstractConfigValue
67
173
  Hocon::Impl::SimpleConfigOrigin.merge_origins(origins)
68
174
  end
69
175
 
70
- def peek_assuming_resolved(key, original_path)
71
- begin
72
- attempt_peek_with_partial_resolve(key)
73
- rescue ConfigNotResolvedError => e
74
- raise Hocon::Impl::ConfigImpl.improve_not_resolved(original_path, e)
75
- end
176
+ def resolve_substitutions(context, source)
177
+ raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `resolve_substituions`"
76
178
  end
77
179
 
78
- def peek_path(path)
79
- begin
80
- return peek_path_impl(self, path, nil)
81
- rescue ConfigNotResolvedError => e
82
- raise ConfigBugOrBrokenError.new("NotPossibleToResolve happened though we had no ResolveContext in peek_path", nil)
83
- end
180
+ def relativized(path)
181
+ raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `relativized`"
84
182
  end
85
183
 
86
- def peek_path_impl(me, path, context)
87
- begin
88
- if not context.nil?
89
- partially_resolved = context.restrict(path).resolve(me)
90
- if partially_resolved.is_a?(self.class)
91
- return peek_path_impl(partially_resolved, path, nil)
92
- else
93
- raise ConfigBugOrBrokenError.new(
94
- "resolved object to non-object #{me} to #{partially_resolved}", nil)
95
- end
96
- else
97
- remainder = path.remainder
98
- v = me.attempt_peek_with_partial_resolve(path.first)
99
- if remainder.nil?
100
- return v
101
- else
102
- if v.is_a?(self.class)
103
- return peek_path_impl(v, remainder, nil)
104
- else
105
- return nil
106
- end
107
- end
108
- end
109
- rescue ConfigNotResolvedError => e
110
- raise Hocon::Impl::ConfigImpl.improve_not_resolved(path, e)
111
- end
184
+ def [](key)
185
+ raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `[]`"
186
+ end
187
+
188
+ def render_value_to_sb(sb, indent, at_root, options)
189
+ raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `render_value_to_sb`"
190
+ end
191
+
192
+ def we_are_immutable(method)
193
+ Hocon::Impl::UnsupportedOperationError.new("ConfigObject is immutable, you can't call Map.#{method}")
194
+ end
195
+
196
+ def clear
197
+ raise we_are_immutable("clear")
198
+ end
199
+
200
+ def []=(key, value)
201
+ raise we_are_immutable("[]=")
202
+ end
203
+
204
+ def putAll(map)
205
+ raise we_are_immutable("putAll")
206
+ end
207
+
208
+ def remove(key)
209
+ raise we_are_immutable("remove")
210
+ end
211
+
212
+ def delete(key)
213
+ raise we_are_immutable("delete")
214
+ end
215
+
216
+ def with_origin(origin)
217
+ super(origin)
112
218
  end
113
- end
219
+ end
@@ -1,19 +1,27 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'hocon/impl'
2
4
  require 'stringio'
3
5
  require 'hocon/config_render_options'
4
6
  require 'hocon/config_object'
5
7
  require 'hocon/impl/resolve_status'
8
+ require 'hocon/impl/resolve_result'
6
9
  require 'hocon/impl/unmergeable'
7
- require 'hocon/impl/abstract_config_object'
8
10
  require 'hocon/impl/config_impl_util'
11
+ require 'hocon/config_error'
12
+ require 'hocon/config_value'
9
13
 
10
14
  ##
11
15
  ## Trying very hard to avoid a parent reference in config values; when you have
12
16
  ## a tree like this, the availability of parent() tends to result in a lot of
13
17
  ## improperly-factored and non-modular code. Please don't add parent().
14
18
  ##
15
- class Hocon::Impl::AbstractConfigValue
19
+ module Hocon::Impl::AbstractConfigValue
20
+ include Hocon::ConfigValue
21
+
16
22
  ConfigImplUtil = Hocon::Impl::ConfigImplUtil
23
+ ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
24
+ ResolveStatus = Hocon::Impl::ResolveStatus
17
25
 
18
26
  def initialize(origin)
19
27
  @origin = origin
@@ -21,10 +29,110 @@ class Hocon::Impl::AbstractConfigValue
21
29
 
22
30
  attr_reader :origin
23
31
 
32
+ # This exception means that a value is inherently not resolveable, at the
33
+ # moment the only known cause is a cycle of substitutions. This is a
34
+ # checked exception since it's internal to the library and we want to be
35
+ # sure we handle it before passing it out to public API. This is only
36
+ # supposed to be thrown by the target of a cyclic reference and it's
37
+ # supposed to be caught by the ConfigReference looking up that reference,
38
+ # so it should be impossible for an outermost resolve() to throw this.
39
+ #
40
+ # Contrast with ConfigException.NotResolved which just means nobody called
41
+ # resolve().
42
+ class NotPossibleToResolve < Exception
43
+ def initialize(context)
44
+ super("was not possible to resolve")
45
+ @trace_string = context.trace_string
46
+ end
47
+
48
+ attr_reader :trace_string
49
+ end
50
+
51
+ # Called only by ResolveContext.resolve
52
+ #
53
+ # @param context
54
+ # state of the current resolve
55
+ # @param source
56
+ # where to look up values
57
+ # @return a new value if there were changes, or this if no changes
58
+ def resolve_substitutions(context, source)
59
+ Hocon::Impl::ResolveResult.make(context, self)
60
+ end
61
+
24
62
  def resolve_status
25
63
  Hocon::Impl::ResolveStatus::RESOLVED
26
64
  end
27
65
 
66
+ def self.replace_child_in_list(list, child, replacement)
67
+ i = 0
68
+ while (i < list.size) && (! list[i].equal?(child))
69
+ i += 1
70
+ end
71
+ if (i == list.size)
72
+ raise ConfigBugOrBrokenError, "tried to replace #{child} which is not in #{list}"
73
+ end
74
+
75
+ new_stack = list.clone
76
+ if ! replacement.nil?
77
+ new_stack[i] = replacement
78
+ else
79
+ new_stack.delete(i)
80
+ end
81
+
82
+ if new_stack.empty?
83
+ nil
84
+ else
85
+ new_stack
86
+ end
87
+ end
88
+
89
+ def self.has_descendant_in_list?(list, descendant)
90
+ list.each do |v|
91
+ if v.equal?(descendant)
92
+ return true
93
+ end
94
+ end
95
+ # now the expensive traversal
96
+ list.each do |v|
97
+ if v.is_a?(Hocon::Impl::Container) && v.has_descendant?(descendant)
98
+ return true
99
+ end
100
+ end
101
+ false
102
+ end
103
+
104
+ # This is used when including one file in another; the included file is
105
+ # relativized to the path it's included into in the parent file. The point
106
+ # is that if you include a file at foo.bar in the parent, and the included
107
+ # file as a substitution ${a.b.c}, the included substitution now needs to
108
+ # be ${foo.bar.a.b.c} because we resolve substitutions globally only after
109
+ # parsing everything.
110
+ #
111
+ # @param prefix
112
+ # @return value relativized to the given path or the same value if nothing
113
+ # to do
114
+ def relativized(prefix)
115
+ self
116
+ end
117
+
118
+ module NoExceptionsModifier
119
+ def modify_child_may_throw(key_or_nil, v)
120
+ begin
121
+ modify_child(key_or_nil, v)
122
+ rescue Hocon::ConfigError => e
123
+ raise e
124
+ end
125
+ end
126
+ end
127
+
128
+ def to_fallback_value
129
+ self
130
+ end
131
+
132
+ def new_copy(origin)
133
+ raise ConfigBugOrBrokenError, "subclasses of AbstractConfigValue should provide their own implementation of `new_copy` (#{self.class})"
134
+ end
135
+
28
136
  # this is virtualized rather than a field because only some subclasses
29
137
  # really need to store the boolean, and they may be able to pack it
30
138
  # with another boolean to save space.
@@ -34,16 +142,97 @@ class Hocon::Impl::AbstractConfigValue
34
142
  resolve_status == Hocon::Impl::ResolveStatus::RESOLVED
35
143
  end
36
144
 
145
+ def with_fallbacks_ignored
146
+ if ignores_fallbacks?
147
+ self
148
+ else
149
+ raise ConfigBugOrBrokenError, "value class doesn't implement forced fallback-ignoring #{self}"
150
+ end
151
+ end
152
+
37
153
  # the withFallback() implementation is supposed to avoid calling
38
154
  # mergedWith* if we're ignoring fallbacks.
39
155
  def require_not_ignoring_fallbacks
40
156
  if ignores_fallbacks?
41
- raise ConfigBugError, "method should not have been called with ignoresFallbacks=true #{self.class.name}"
157
+ raise ConfigBugOrBrokenError, "method should not have been called with ignoresFallbacks=true #{self.class.name}"
42
158
  end
43
159
  end
44
160
 
161
+ def construct_delayed_merge(origin, stack)
162
+ # TODO: this might not work because ConfigDelayedMerge inherits
163
+ # from this class, so we can't `require` it from this file
164
+ require 'hocon/impl/config_delayed_merge'
165
+ Hocon::Impl::ConfigDelayedMerge.new(origin, stack)
166
+ end
167
+
168
+ def merged_stack_with_the_unmergeable(stack, fallback)
169
+ require_not_ignoring_fallbacks
170
+
171
+ # if we turn out to be an object, and the fallback also does,
172
+ # then a merge may be required; delay until we resolve.
173
+ new_stack = stack.clone
174
+ new_stack.concat(fallback.unmerged_values)
175
+ # TODO: this might not work because AbstractConfigObject inherits
176
+ # from this class, so we can't `require` it from this file
177
+ construct_delayed_merge(Hocon::Impl::AbstractConfigObject.merge_origins(new_stack), new_stack)
178
+ end
179
+
180
+ def delay_merge(stack, fallback)
181
+ # if we turn out to be an object, and the fallback also does,
182
+ # then a merge may be required.
183
+ # if we contain a substitution, resolving it may need to look
184
+ # back to the fallback
185
+ new_stack = stack.clone
186
+ new_stack << fallback
187
+ # TODO: this might not work because AbstractConfigObject inherits
188
+ # from this class, so we can't `require` it from this file
189
+ construct_delayed_merge(Hocon::Impl::AbstractConfigObject.merge_origins(new_stack), new_stack)
190
+ end
191
+
192
+ def merged_stack_with_object(stack, fallback)
193
+ require_not_ignoring_fallbacks
194
+
195
+ # TODO: this might not work because AbstractConfigObject inherits
196
+ # from this class, so we can't `require` it from this file
197
+ if self.is_a?(Hocon::Impl::AbstractConfigObject)
198
+ raise ConfigBugOrBrokenError, "Objects must reimplement merged_with_object"
199
+ end
200
+
201
+ merged_stack_with_non_object(stack, fallback)
202
+ end
203
+
204
+ def merged_stack_with_non_object(stack, fallback)
205
+ require_not_ignoring_fallbacks
206
+
207
+ if resolve_status == ResolveStatus::RESOLVED
208
+ # falling back to a non-object doesn't merge anything, and also
209
+ # prohibits merging any objects that we fall back to later.
210
+ # so we have to switch to ignoresFallbacks mode.
211
+ with_fallbacks_ignored
212
+ else
213
+ # if unresolved we may have to look back to fallbacks as part of
214
+ # the resolution process, so always delay
215
+ delay_merge(stack, fallback)
216
+ end
217
+ end
218
+
219
+ def merged_with_the_unmergeable(fallback)
220
+ require_not_ignoring_fallbacks
221
+ merged_stack_with_the_unmergeable([self], fallback)
222
+ end
223
+
224
+ def merged_with_object(fallback)
225
+ require_not_ignoring_fallbacks
226
+ merged_stack_with_object([self], fallback)
227
+ end
228
+
229
+ def merged_with_non_object(fallback)
230
+ require_not_ignoring_fallbacks
231
+ merged_stack_with_non_object([self], fallback)
232
+ end
233
+
45
234
  def with_origin(origin)
46
- if @origin == origin
235
+ if @origin.equal?(origin)
47
236
  self
48
237
  else
49
238
  new_copy(origin)
@@ -57,6 +246,8 @@ class Hocon::Impl::AbstractConfigValue
57
246
  other = mergeable.to_fallback_value
58
247
  if other.is_a?(Hocon::Impl::Unmergeable)
59
248
  merged_with_the_unmergeable(other)
249
+ # TODO: this probably isn't going to work because AbstractConfigObject inherits
250
+ # from this class, so we can't `require` it from this file
60
251
  elsif other.is_a?(Hocon::Impl::AbstractConfigObject)
61
252
  merged_with_object(other)
62
253
  else
@@ -65,13 +256,42 @@ class Hocon::Impl::AbstractConfigValue
65
256
  end
66
257
  end
67
258
 
259
+ def can_equal(other)
260
+ other.is_a?(Hocon::Impl::AbstractConfigValue)
261
+ end
262
+
263
+ def ==(other)
264
+ # note that "origin" is deliberately NOT part of equality
265
+ if other.is_a?(Hocon::Impl::AbstractConfigValue)
266
+ can_equal(other) &&
267
+ value_type == other.value_type &&
268
+ ConfigImplUtil.equals_handling_nil?(unwrapped, other.unwrapped)
269
+ else
270
+ false
271
+ end
272
+ end
273
+
274
+ def hash
275
+ # note that "origin" is deliberately NOT part of equality
276
+ unwrapped_value = unwrapped
277
+ if unwrapped_value.nil?
278
+ 0
279
+ else
280
+ unwrapped_value.hash
281
+ end
282
+ end
283
+
68
284
  def to_s
69
285
  sb = StringIO.new
70
286
  render_to_sb(sb, 0, true, nil, Hocon::ConfigRenderOptions.concise)
71
- "#{self.class.name}(#{sb.string})"
287
+ "#{self.class.name.split('::').last}(#{sb.string})"
72
288
  end
73
289
 
74
- def indent(sb, indent_size, options)
290
+ def inspect
291
+ to_s
292
+ end
293
+
294
+ def self.indent(sb, indent_size, options)
75
295
  if options.formatted?
76
296
  remaining = indent_size
77
297
  while remaining > 0
@@ -127,20 +347,40 @@ class Hocon::Impl::AbstractConfigValue
127
347
  sb.string[0, sb.pos]
128
348
  end
129
349
 
130
- def at_key(origin, key)
350
+ # toString() is a debugging-oriented string but this is defined
351
+ # to create a string that would parse back to the value in JSON.
352
+ # It only works for primitive values (that would be a single
353
+ # which are auto-converted to strings when concatenating with
354
+ # other strings or by the DefaultTransformer.
355
+ def transform_to_string
356
+ nil
357
+ end
358
+
359
+ def at_key(key)
360
+ at_key_with_origin(Hocon::Impl::SimpleConfigOrigin.new_simple("at_key(#{key})"), key)
361
+ end
362
+
363
+ # Renamed this to be consistent with the other at_key* overloaded methods
364
+ def at_key_with_origin(origin, key)
131
365
  m = {key=>self}
132
366
  Hocon::Impl::SimpleConfigObject.new(origin, m).to_config
133
367
  end
134
368
 
135
- def at_path(origin, path)
369
+ # In java this is an overloaded version of atPath
370
+ def at_path_with_origin(origin, path)
136
371
  parent = path.parent
137
- result = at_key(origin, path.last)
372
+ result = at_key_with_origin(origin, path.last)
138
373
  while not parent.nil? do
139
374
  key = parent.last
140
- result = result.at_key(origin, key)
375
+ result = result.at_key_with_origin(origin, key)
141
376
  parent = parent.parent
142
377
  end
143
378
  result
144
379
  end
145
380
 
146
- end
381
+ def at_path(path_expression)
382
+ origin = Hocon::Impl::SimpleConfigOrigin.new_simple("at_path(#{path_expression})")
383
+ at_path_with_origin(origin, Hocon::Impl::Path.new_path(path_expression))
384
+ end
385
+
386
+ end