Class: Hocon::Impl::Parseable

Inherits:
Object
  • Object
show all
Includes:
ConfigParseable
Defined in:
lib/hocon/impl/parseable.rb

Overview

Internal implementation detail, not ABI stable, do not touch. For use only by the com.typesafe.config package. The point of this class is to avoid “propagating” each overload on “thing which can be parsed” through multiple interfaces. Most interfaces can have just one overload that takes a Parseable. Also it’s used as an abstract “resource handle” in the ConfigIncluder interface.

Defined Under Namespace

Modules: Relativizer Classes: ParseableFile, ParseableNotFound, ParseableResources, ParseableString

Constant Summary collapse

MAX_INCLUDE_DEPTH =
50

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeParseable

Returns a new instance of Parseable.



46
47
48
# File 'lib/hocon/impl/parseable.rb', line 46

def initialize

end

Class Method Details

.force_parsed_to_object(value) ⇒ Object



123
124
125
126
127
128
129
130
131
132
# File 'lib/hocon/impl/parseable.rb', line 123

def self.force_parsed_to_object(value)
  if value.is_a? Hocon::Impl::AbstractConfigObject
    value
  else
    raise Hocon::ConfigError::ConfigWrongTypeError.with_expected_actual(value.origin,
                                                       "",
                                                       "object at file root",
                                                       Hocon::ConfigValueType.value_type_name(value.value_type))
  end
end

.new_file(file_path, options) ⇒ Object



465
466
467
# File 'lib/hocon/impl/parseable.rb', line 465

def self.new_file(file_path, options)
  ParseableFile.new(file_path, options)
end

.new_not_found(what_not_found, message, options) ⇒ Object



349
350
351
# File 'lib/hocon/impl/parseable.rb', line 349

def self.new_not_found(what_not_found, message, options)
  ParseableNotFound.new(what_not_found, message, options)
end

.new_resources(resource, options) ⇒ Object



545
546
547
# File 'lib/hocon/impl/parseable.rb', line 545

def self.new_resources(resource, options)
  ParseableResources.new(resource, options)
end

.new_string(string, options) ⇒ Object



391
392
393
# File 'lib/hocon/impl/parseable.rb', line 391

def self.new_string(string, options)
  ParseableString.new(string, options)
end

.parse_stackObject

The upstream library seems to use this as a global way of keeping track of how many files have been included, to avoid cycles



40
41
42
# File 'lib/hocon/impl/parseable.rb', line 40

def self.parse_stack
  Thread.current[:hocon_parse_stack] ||= []
end

.relative_to(file, filename) ⇒ Object

NOTE: skipping ‘relativeTo(URL, String)` because we’re not supporting URLs for now



314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/hocon/impl/parseable.rb', line 314

def self.relative_to(file, filename)
  child = Pathname.new(filename)
  file = Pathname.new(file)

  if child.absolute?
    nil
  end

  parent = file.parent

  if parent.nil?
    nil
  else
    File.join(parent, filename)
  end
end

.syntax_from_extension(name) ⇒ Object



300
301
302
303
304
305
306
307
308
309
# File 'lib/hocon/impl/parseable.rb', line 300

def self.syntax_from_extension(name)
  if name.end_with?(".json")
    Hocon::ConfigSyntax::JSON
  elsif name.end_with?(".conf")
    Hocon::ConfigSyntax::CONF
  else
    # Skipping PROPERTIES because we can't really support that in ruby
    nil
  end
end

.trace(message) ⇒ Object



93
94
95
96
97
# File 'lib/hocon/impl/parseable.rb', line 93

def self.trace(message)
  if Hocon::Impl::ConfigImpl.trace_loads_enabled
    Hocon::Impl::ConfigImpl.trace(message)
  end
end

Instance Method Details

#content_typeObject



103
104
105
# File 'lib/hocon/impl/parseable.rb', line 103

def content_type
  nil
end

#create_originObject



288
289
290
# File 'lib/hocon/impl/parseable.rb', line 288

def create_origin
  raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Parseable` must implement `create_origin` (#{self.class})"
end

#custom_readerObject

the general idea is that any work should be in here, not in the constructor, so that exceptions are thrown from the public parse() function and not from the creation of the Parseable. Essentially this is a lazy field. The parser should close the reader when it’s done with it. //# ALSO, IMPORTANT: if the file or URL is not found, this must throw. //# to support the “allow missing” feature.



85
86
87
# File 'lib/hocon/impl/parseable.rb', line 85

def custom_reader
  raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Parseable` must implement `custom_reader` (#{self.class})"
end

#fixup_options(base_options) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/hocon/impl/parseable.rb', line 50

def fixup_options(base_options)
  syntax = base_options.syntax
  if !syntax
    syntax = guess_syntax
  end
  if !syntax
    syntax = Hocon::ConfigSyntax::CONF
  end
  modified = base_options.set_syntax(syntax)

  # make sure the app-provided includer falls back to default
  modified = modified.append_includer(Hocon::Impl::ConfigImpl.default_includer)
  # make sure the app-provided includer is complete
  modified = modified.set_includer(Hocon::Impl::SimpleIncluder.make_full(modified.includer))

  modified
end

#guess_syntaxObject



99
100
101
# File 'lib/hocon/impl/parseable.rb', line 99

def guess_syntax
  nil
end

#include_contextObject



119
120
121
# File 'lib/hocon/impl/parseable.rb', line 119

def include_context
  @include_context
end

#optionsObject



292
293
294
# File 'lib/hocon/impl/parseable.rb', line 292

def options
  @initial_options
end

#originObject



284
285
286
# File 'lib/hocon/impl/parseable.rb', line 284

def origin
  @initial_origin
end

#parse(base_options = nil) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/hocon/impl/parseable.rb', line 134

def parse(base_options = nil)
  if (base_options.nil?)
    base_options = options
  end
  stack = self.class.parse_stack
  if stack.length >= MAX_INCLUDE_DEPTH
    raise Hocon::ConfigError::ConfigParseError.new(@initial_origin,
          "include statements nested more than #{MAX_INCLUDE_DEPTH} times, " +
          "you probably have a cycle in your includes.  Trace: #{stack}",
          nil)
  end

  # Push into beginning of stack
  stack.unshift(self)
  begin
    self.class.force_parsed_to_object(parse_value(base_options))
  ensure
    # Pop from beginning of stack
    stack.shift
  end
end

#parse_config_documentObject



280
281
282
# File 'lib/hocon/impl/parseable.rb', line 280

def parse_config_document
  parse_document(options)
end

#parse_document(base_options = nil) ⇒ Object



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/hocon/impl/parseable.rb', line 190

def parse_document(base_options = nil)
  if base_options.nil?
    base_options = options
  end

  # note that we are NOT using our "initialOptions",
  # but using the ones from the passed-in options. The idea is that
  # callers can get our original options and then parse with different
  # ones if they want.
  options = fixup_options(base_options)

  # passed-in option can override origin
  origin = nil
  if ! options.origin_description.nil?
    origin = Hocon::Impl::SimpleConfigOrigin.new_simple(options.origin_description)
  else
    origin = @initial_origin
  end
  parse_document_from_origin(origin, options)
end

#parse_document_from_origin(origin, final_options) ⇒ Object



211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/hocon/impl/parseable.rb', line 211

def parse_document_from_origin(origin, final_options)
  begin
    raw_parse_document(origin, final_options)
  rescue IOError => e
    if final_options.allow_missing?
      Hocon::Impl::SimpleConfigDocument.new(
        Hocon::Impl::ConfigNodeObject.new([]), final_options)
    else
      self.class.trace("exception loading #{origin.description}: #{e.class}: #{e.message}")
      raise ConfigIOError.new(origin, "#{e.class.name}: #{e.message}", e)
    end
  end
end

#parse_value(base_options = nil) ⇒ Object



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/hocon/impl/parseable.rb', line 156

def parse_value(base_options = nil)
  if base_options.nil?
    base_options = options
  end

  # note that we are NOT using our "initialOptions",
  # but using the ones from the passed-in options. The idea is that
  # callers can get our original options and then parse with different
  # ones if they want.
  options = fixup_options(base_options)

  # passed-in options can override origin
  origin =
      if options.origin_description
        Hocon::Impl::SimpleConfigOrigin.new_simple(options.origin_description)
      else
        @initial_origin
      end
  parse_value_from_origin(origin, options)
end

#parse_value_from_origin(origin, final_options) ⇒ Object



177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/hocon/impl/parseable.rb', line 177

def parse_value_from_origin(origin, final_options)
  begin
    raw_parse_value(origin, final_options)
  rescue IOError => e
    if final_options.allow_missing?
      Hocon::Impl::SimpleConfigObject.empty_missing(origin)
    else
      self.class.trace("exception loading #{origin.description}: #{e.class}: #{e.message}")
      raise Hocon::ConfigError::ConfigIOError.new(origin, "#{e.class.name}: #{e.message}", e)
    end
  end
end

#post_construct(base_options) ⇒ Object



68
69
70
71
72
73
74
75
76
# File 'lib/hocon/impl/parseable.rb', line 68

def post_construct(base_options)
  @initial_options = fixup_options(base_options)
  @include_context = Hocon::Impl::SimpleIncludeContext.new(self)
  if @initial_options.origin_description
    @initial_origin = Hocon::Impl::SimpleConfigOrigin.new_simple(@initial_options.origin_description)
  else
    @initial_origin = create_origin
  end
end

#raw_parse_document(origin, final_options) ⇒ Object



254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/hocon/impl/parseable.rb', line 254

def raw_parse_document(origin, final_options)
  reader = reader(final_options)
  content_type = content_type()

  options_with_content_type = nil
  if !(content_type.nil?)
    if Hocon::Impl::ConfigImpl.trace_loads_enabled && (! final_options.get_syntax.nil?)
      self.class.trace("Overriding syntax #{final_options.get_syntax} with Content-Type which specified #{content-type}")
    end
    options_with_content_type = final_options.set_syntax(content_type)
  else
    options_with_content_type = final_options
  end

  reader.open { |io|
    raw_parse_document_from_io(io, origin, options_with_content_type)
  }
end

#raw_parse_document_from_io(reader, origin, final_options) ⇒ Object



273
274
275
276
277
278
# File 'lib/hocon/impl/parseable.rb', line 273

def raw_parse_document_from_io(reader, origin, final_options)
  tokens = Hocon::Impl::Tokenizer.tokenize(origin, reader, final_options.syntax)
  Hocon::Impl::SimpleConfigDocument.new(
                 Hocon::Impl::ConfigDocumentParser.parse(tokens, origin, final_options),
                 final_options)
end

#raw_parse_value(origin, final_options) ⇒ Object

this is parseValue without post-processing the IOException or handling options.getAllowMissing()



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/hocon/impl/parseable.rb', line 227

def raw_parse_value(origin, final_options)
  reader = reader(final_options)

  # after reader() we will have loaded the Content-Type
  content_type = content_type()

  options_with_content_type = nil
  if !(content_type.nil?)
    if Hocon::Impl::ConfigImpl.trace_loads_enabled && (! final_options.get_syntax.nil?)
      self.class.trace("Overriding syntax #{final_options.get_syntax} with Content-Type which specified #{content-type}")
    end
    options_with_content_type = final_options.set_syntax(content_type)
  else
    options_with_content_type = final_options
  end

  reader.open { |io|
    raw_parse_value_from_io(io, origin, options_with_content_type)
  }
end

#raw_parse_value_from_io(io, origin, final_options) ⇒ Object



248
249
250
251
252
# File 'lib/hocon/impl/parseable.rb', line 248

def raw_parse_value_from_io(io, origin, final_options)
  tokens = Hocon::Impl::Tokenizer.tokenize(origin, io, final_options.syntax)
  document = Hocon::Impl::ConfigDocumentParser.parse(tokens, origin, final_options)
  Hocon::Impl::ConfigParser.parse(document, origin, final_options, include_context)
end

#reader(options) ⇒ Object



89
90
91
# File 'lib/hocon/impl/parseable.rb', line 89

def reader(options)
  custom_reader
end

#relative_to(filename) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
# File 'lib/hocon/impl/parseable.rb', line 107

def relative_to(filename)
  # fall back to classpath; we treat the "filename" as absolute
  # (don't add a package name in front),
  # if it starts with "/" then remove the "/", for consistency
  # with ParseableResources.relativeTo
  resource = filename
  if filename.start_with?("/")
    resource = filename.slice(1)
  end
  self.class.new_resources(resource, options.set_origin_description(nil))
end

#to_sObject



296
297
298
# File 'lib/hocon/impl/parseable.rb', line 296

def to_s
  self.class.name.split('::').last
end