File: simple_includer.rb

package info (click to toggle)
ruby-hocon 1.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 768 kB
  • sloc: ruby: 7,903; makefile: 4
file content (214 lines) | stat: -rw-r--r-- 6,934 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# encoding: utf-8

require 'stringio'
require_relative '../../hocon/impl'
require_relative '../../hocon/impl/full_includer'
require_relative '../../hocon/impl/url'
require_relative '../../hocon/impl/config_impl'
require_relative '../../hocon/config_error'
require_relative '../../hocon/config_syntax'
require_relative '../../hocon/impl/simple_config_object'
require_relative '../../hocon/impl/simple_config_origin'
require_relative '../../hocon/config_includer_file'
require_relative '../../hocon/config_factory'
require_relative '../../hocon/impl/parseable'

class Hocon::Impl::SimpleIncluder < Hocon::Impl::FullIncluder

  ConfigBugorBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
  ConfigIOError = Hocon::ConfigError::ConfigIOError
  SimpleConfigObject = Hocon::Impl::SimpleConfigObject
  SimpleConfigOrigin = Hocon::Impl::SimpleConfigOrigin


  def initialize(fallback)
    @fallback = fallback
  end

  # ConfigIncludeContext does this for us on its options
  def self.clear_for_include(options)
    # the class loader and includer are inherited, but not this other stuff
    options.set_syntax(nil).set_origin_description(nil).set_allow_missing(true)
  end


  # this is the heuristic includer
  def include(context, name)
    obj = self.class.include_without_fallback(context, name)

    # now use the fallback includer if any and merge its result
    if ! (@fallback.nil?)
      obj.with_fallback(@fallback.include(context, name))
    else
      obj
    end
  end

  # the heuristic includer in static form
  def self.include_without_fallback(context, name)
    # the heuristic is valid URL then URL, else relative to including file;
    # relativeTo in a file falls back to classpath inside relativeTo().

    url = nil
    begin
      url = Hocon::Impl::Url.new(name)
    rescue Hocon::Impl::Url::MalformedUrlError => e
      url = nil
    end

    if !(url.nil?)
      include_url_without_fallback(context, url)
    else
      source = RelativeNameSource.new(context)
      from_basename(source, name, context.parse_options)
    end
  end

  # NOTE: not porting `include_url` or `include_url_without_fallback` from upstream,
  #  because we probably won't support URL includes for now.

  def include_file(context, file)
    obj = self.class.include_file_without_fallback(context, file)

    # now use the fallback includer if any and merge its result
    if (!@fallback.nil?) && @fallback.is_a?(Hocon::ConfigIncluderFile)
      obj.with_fallback(@fallback).include_file(context, file)
    else
      obj
    end
  end

  def self.include_file_without_fallback(context, file)
    Hocon::ConfigFactory.parse_file_any_syntax(file, context.parse_options).root
  end

  # NOTE: not porting `include_resources` or `include_resources_without_fallback`
  # for now because we're not going to support looking for things on the ruby
  # load path for now.

  def with_fallback(fallback)
    if self.equal?(fallback)
      raise ConfigBugOrBrokenError, "trying to create includer cycle"
    elsif @fallback.equal?(fallback)
      self
    elsif @fallback.nil?
      self.class.new(@fallback.with_fallback(fallback))
    else
      self.class.new(fallback)
    end
  end


  class NameSource
    def name_to_parseable(name, parse_options)
      raise Hocon::ConfigError::ConfigBugOrBrokenError,
            "name_to_parseable must be implemented by subclass (#{self.class})"
    end
  end

  class RelativeNameSource < NameSource
    def initialize(context)
      @context = context
    end

    def name_to_parseable(name, options)
      p = @context.relative_to(name)
      if p.nil?
        # avoid returning nil
        Hocon::Impl::Parseable.new_not_found(name, "include was not found: '#{name}'", options)
      else
        p
      end
    end
  end

  # this function is a little tricky because there are three places we're
  # trying to use it; for 'include "basename"' in a .conf file, for
  # loading app.{conf,json,properties} from classpath, and for
  # loading app.{conf,json,properties} from the filesystem.
  def self.from_basename(source, name, options)
    obj = nil
    if name.end_with?(".conf") || name.end_with?(".json") || name.end_with?(".properties")
      p = source.name_to_parseable(name, options)

      obj = p.parse(p.options.set_allow_missing(options.allow_missing?))
    else
      conf_handle = source.name_to_parseable(name + ".conf", options)
      json_handle = source.name_to_parseable(name + ".json", options)
      got_something = false
      fails = []

      syntax = options.syntax

      obj = SimpleConfigObject.empty(SimpleConfigOrigin.new_simple(name))
      if syntax.nil? || (syntax == Hocon::ConfigSyntax::CONF)
        begin
          obj = conf_handle.parse(conf_handle.options.set_allow_missing(false).
                  set_syntax(Hocon::ConfigSyntax::CONF))
          got_something = true
        rescue ConfigIOError => e
          fails << e
        end
      end

      if syntax.nil? || (syntax == Hocon::ConfigSyntax::JSON)
        begin
          parsed = json_handle.parse(json_handle.options.set_allow_missing(false).
                                         set_syntax(Hocon::ConfigSyntax::JSON))
          obj = obj.with_fallback(parsed)
          got_something = true
        rescue ConfigIOError => e
          fails << e
        end
      end

      # NOTE: skipping the upstream block here that would attempt to parse
      # a java properties file.

      if (! options.allow_missing?) && (! got_something)
        if Hocon::Impl::ConfigImpl.trace_loads_enabled
          # the individual exceptions should have been logged already
          # with tracing enabled
          Hocon::Impl::ConfigImpl.trace("Did not find '#{name}'" +
            " with any extension (.conf, .json, .properties); " +
            "exceptions should have been logged above.")
        end

        if fails.empty?
          # this should not happen
          raise ConfigBugOrBrokenError, "should not be reached: nothing found but no exceptions thrown"
        else
          sb = StringIO.new
          fails.each do |t|
            sb << t
            sb << ", "
          end
          raise ConfigIOError.new(SimpleConfigOrigin.new_simple(name), sb.string, fails[0])
        end
      elsif !got_something
        if Hocon::Impl::ConfigImpl.trace_loads_enabled
          Hocon::Impl::ConfigImpl.trace("Did not find '#{name}'" +
            " with any extension (.conf, .json, .properties); but '#{name}'" +
            " is allowed to be missing. Exceptions from load attempts should have been logged above.")
        end
      end
    end

    obj
  end

  class Proxy < Hocon::Impl::FullIncluder
    def initialize(delegate)
      @delegate = delegate
    end
    ## TODO: port remaining implementation when needed
  end

  def self.make_full(includer)
    if includer.is_a?(Hocon::Impl::FullIncluder)
      includer
    else
      Proxy.new(includer)
    end
  end
end