Class: Bogo::Config

Inherits:
Object
  • Object
show all
Extended by:
Memoization, Forwardable
Includes:
Lazy
Defined in:
lib/bogo-config/config.rb,
lib/bogo-config/version.rb

Defined Under Namespace

Classes: FileLoadError

Constant Summary collapse

VERSION =

Current library version

Gem::Version.new('0.2.2')

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path_or_hash = nil) ⇒ self

Create new instance

Parameters:

  • path_or_hash (String, Hash) (defaults to: nil)

    file/directory path or base Hash



111
112
113
114
115
# File 'lib/bogo-config/config.rb', line 111

def initialize(path_or_hash=nil)
  @initial = path_or_hash
  @data = Smash.new
  init!
end

Instance Attribute Details

#initialString, Hash (readonly)

Returns:

  • (String, Hash)


105
106
107
# File 'lib/bogo-config/config.rb', line 105

def initial
  @initial
end

#pathString (readonly)

Returns configuration path.

Returns:

  • (String)

    configuration path



103
104
105
# File 'lib/bogo-config/config.rb', line 103

def path
  @path
end

Class Method Details

.reload!TrueClass

Reload any registered ‘Bogo::Config` instances

Returns:

  • (TrueClass)


38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/bogo-config/config.rb', line 38

def reload!
  obj_ids = memoize(:bogo_reloadable_configs, :global)
  objects = Thread.exclusive do
    ObjectSpace.each_object.find_all do |obj|
      obj_ids.include?(obj.object_id)
    end
  end
  objects.map(&:init!)
  memoize(:bogo_reloadable_configs, :global).delete_if do |oid|
    !obj_ids.include?(oid)
  end
  true
end

.reloadable(config) ⇒ TrueClass

Register config instance to auto reload on HUP

Parameters:

Returns:

  • (TrueClass)


56
57
58
59
60
61
62
63
64
# File 'lib/bogo-config/config.rb', line 56

def reloadable(config)
  if(config.is_a?(Bogo::Config))
    reloader
    memoize(:bogo_reloadable_configs, :global){ [] }.push(config.object_id).uniq!
  else
    raise TypeError.new "Expecting type `Bogo::Config`. Received: `#{config.class}`"
  end
  true
end

.reloaderThread

Internal reloader

Returns:

  • (Thread)


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
# File 'lib/bogo-config/config.rb', line 69

def reloader
  memoize(:bogo_config_reloader, :global) do
    Thread.new do
      begin
        loop do
          begin
            sleep
          rescue SignalException => e
            if(e.signm == 'SIGHUP')
              if(ENV['BOGO_DEBUG'])
                $stdout.puts 'SIGHUP encountered. Reloading `Bogo::Config` instances.'
              end
              Bogo::Config.reload!
            else
              raise
            end
          end
        end
      rescue => e
        if(ENV['BOGO_DEBUG'])
          $stderr.puts "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
        end
        retry
      end
    end
  end
end

Instance Method Details

#dirtySmash

Override to force consistent data access (removes dirty functionality)

Returns:

  • (Smash)


159
160
161
# File 'lib/bogo-config/config.rb', line 159

def dirty
  data
end

#eval_disabled?TrueClass, FalseClass

Returns:

  • (TrueClass, FalseClass)


310
311
312
# File 'lib/bogo-config/config.rb', line 310

def eval_disabled?
  !eval_enabled?
end

#eval_enabled?TrueClass, FalseClass

Returns:

  • (TrueClass, FalseClass)


305
306
307
# File 'lib/bogo-config/config.rb', line 305

def eval_enabled?
  ENV['BOGO_CONFIG_DISABLE_EVAL'].to_s.downcase != 'true'
end

#extract_error_for(path, errors) ⇒ Exception, NilClass

Extract appropriate execption based on path

Parameters:

  • path (String)
  • errors (Hash)

Returns:

  • (Exception, NilClass)


319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/bogo-config/config.rb', line 319

def extract_error_for(path, errors)
  content = File.read(path).strip
  if(content.start_with?('{'))
    errors[:json_load]
  elsif(content.start_with?('<'))
    errors[:xml_load]
  elsif(content.match(/\.new\s*(do|\{)/))
    errors[:struct_load]
  else
    errors[:yaml_load]
  end
end

#immutable!self

Freeze underlying configuration data

Returns:

  • (self)


128
129
130
# File 'lib/bogo-config/config.rb', line 128

def immutable!
  @data = data.to_smash(:freeze)
end

#init!self

Intialize the configuration

Returns:

  • (self)


135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/bogo-config/config.rb', line 135

def init!
  if(initial.is_a?(String))
    @path = initial.dup
    hash = load!
  else
    hash = initial
  end
  if(hash)
    is_immutable = data.frozen?
    # TODO: synchronize here
    load_data(hash)
    @data = hash.to_smash.deep_merge(data.to_smash)
    @data.to_smash(:freeze) if is_immutable
  end
  self
end

#json_load(file_path) ⇒ Smash

Read and parse JSON file

Parameters:

  • file_path

Returns:

  • (Smash)


241
242
243
# File 'lib/bogo-config/config.rb', line 241

def json_load(file_path)
  MultiJson.load(File.read(file_path)).to_smash
end

#load!self

Load configuration from file(s)

Returns:

  • (self)


171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/bogo-config/config.rb', line 171

def load!
  if(path)
    if(File.directory?(path))
      conf = Dir.glob(File.join(path, '*')).sort.inject(Smash.new) do |memo, file_path|
        memo.deep_merge(load_file(file_path))
      end
    elsif(File.file?(path))
      conf = load_file(path)
    else
      raise Errno::ENOENT.new path
    end
    conf
  end
end

#load_file(file_path) ⇒ Smash

Load configuration file

Parameters:

  • file_path (String)

    path to file

Returns:

  • (Smash)


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
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/bogo-config/config.rb', line 190

def load_file(file_path)
  result = nil
  errors = Smash.new
  error = nil
  begin
    result = case File.extname(file_path)
             when '.yaml', '.yml'
               yaml_load(file_path)
             when '.json'
               json_load(file_path)
             when '.xml'
               xml_load(file_path)
             when '.rb'
               struct_load(file_path)
             else
               [:struct_load, :json_load, :yaml_load, :xml_load].each do |loader|
                 begin
                   result = send(loader, file_path)
                   break
                 rescue StandardError, ScriptError => e
                   errors[loader] = e
                   if(ENV['BOGO_DEBUG'])
                     $stderr.puts "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
                   end
                 end
               end
               result
             end
  rescue => error
  end
  unless(result)
    raise FileLoadError.new(
      "Failed to load configuration from file (#{file_path})",
      error || extract_error_for(file_path, errors)
    )
  end
  result
end

#reloadable!TrueClass

Enables automatic reloading on SIGHUP

Returns:

  • (TrueClass)


120
121
122
123
# File 'lib/bogo-config/config.rb', line 120

def reloadable!
  Bogo::Config.reloadable(self)
  true
end

#struct_load(file_path) ⇒ Smash

Read and parse AttributeStruct file

Parameters:

  • file_path

Returns:

  • (Smash)


293
294
295
296
297
298
299
300
301
302
# File 'lib/bogo-config/config.rb', line 293

def struct_load(file_path)
  if(eval_disabled?)
    raise 'Ruby based configuration evaluation is currently disabled!'
  else
    result = Module.new.instance_eval(
      IO.read(file_path), file_path, 1
    )
    result._dump.to_smash
  end
end

#to_json(*args) ⇒ String

Returns:

  • (String)


164
165
166
# File 'lib/bogo-config/config.rb', line 164

def to_json(*args)
  MultiJson.dump(data, *args)
end

#xml_format(result) ⇒ Smash

Format XML types

Parameters:

  • result (Smash)

Returns:

  • (Smash)


259
260
261
# File 'lib/bogo-config/config.rb', line 259

def xml_format(result)
  Smash[result.map{|k,v| [k, xml_format_value(v)]}]
end

#xml_format_value(value) ⇒ Object

Format XML value types

Parameters:

  • value (Object)

Returns:

  • (Object)


267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/bogo-config/config.rb', line 267

def xml_format_value(value)
  case value
  when Hash
    xml_format(value)
  when Array
    value.map{|v| xml_format_value(v)}
  else
    value.strip!
    if(value == 'true')
      true
    elsif(value == 'false')
      false
    elsif(value.to_i.to_s == value)
      value.to_i
    elsif(value.to_f.to_s == value)
      value.to_f
    else
      value
    end
  end
end

#xml_load(file_path) ⇒ Smash

Note:

supar ENTERPRISE

Read and parse XML file

Parameters:

  • file_path

Returns:

  • (Smash)


250
251
252
253
# File 'lib/bogo-config/config.rb', line 250

def xml_load(file_path)
  result = MultiXml.parse(File.read(file_path)).to_smash[:configuration]
  xml_format(result)
end

#yaml_load(file_path) ⇒ Smash

Read and parse YAML file

Parameters:

  • file_path

Returns:

  • (Smash)


233
234
235
# File 'lib/bogo-config/config.rb', line 233

def yaml_load(file_path)
  YAML.load(File.read(file_path)).to_smash
end