Module: Configurable::Utils

Defined in:
lib/configurable/utils.rb

Overview

Utility methods to dump and load Configurable class configurations.

Constant Summary collapse

DEFAULT_DUMP =

A block performing the default YAML dump.

default_dump_block
DEFAULT_LOAD =

A block performing the default load.

default_load_block

Class Method Summary collapse

Class Method Details

.dump(configs, target = "") ⇒ Object

Dumps configurations to target as yaml. Configs are output in order, and symbol keys are be stringified if configurations has been extended with IndifferentAccess (this produces a nicer config file).

class DumpExample
  include Configurable

  config :sym, :value      # a symbol config
  config 'str', 'value'    # a string config
end

Utils.dump(DumpExample.configurations, "\n")
# => %q{
# sym: :value
# str: value
# }

Dump may be provided with a block to format each (key, Config) pair; the block results are pushed directly to target, so newlines must be specified manually.

Utils.dump(DumpExample.configurations, "\n") do |key, delegate|
  yaml = YAML.dump({key => delegate.default})[5..-1]
  "# #{delegate[:desc]}\n#{yaml}\n"
end
# => %q{
# # a symbol config
# sym: :value
#
# # a string config
# str: value
#
# }


56
57
58
59
60
61
62
63
64
65
66
# File 'lib/configurable/utils.rb', line 56

def dump(configs, target="")
  return dump(configs, target, &DEFAULT_DUMP) unless block_given?
  
  stringify = configs.kind_of?(IndifferentAccess)
  configs.each_pair do |key, config|
    key = key.to_s if stringify && key.kind_of?(Symbol)
    target << yield(key, config)
  end
  
  target
end

.dump_file(configs, path, recurse = false, preview = false, &block) ⇒ Object

Dumps the configurations to the specified file. If recurse is true, nested configs are each dumped to their own file, based on the nesting key. For instance if you nested a in b:

a_configs = {
  'key' => Config.new(:r, :w, 'a default')}
b_configs = {
  'key' => Config.new(:r, :w, 'b default')}
  'a' => Config.new(:r, :w, ConfigHash.new(a_configs))}}

Utils.dump_file(b_configs, 'b.yml')
File.read('b.yml')         # => "key: b default"
File.read('b/a.yml')       # => "key: a default"

In this way, each nested config gets it’s own file. The load_file method can recursively load configurations from this file structure. When recurse is false, all configs are dumped to a single file.

dump_file uses a method that collects all dumps in a preview array before dumping, so that the dump results can be redirected other places than the file system. If preview is set to false, no files will be created. The preview dumps are always returned by dump_file.

Note

For load_file to correctly load a recursive dump, all config hashes must use String keys. Symbol keys are allowed if the config hashes use IndifferentAccess; all other keys will not load properly. By default Configurable is set up to satisfy these conditions.

1.8 Bug: Currently dump_file with recurse=false will cause order to be lost in nested configs. See bahuvrihi.lighthouseapp.com/projects/21202-configurable/tickets/8



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
# File 'lib/configurable/utils.rb', line 99

def dump_file(configs, path, recurse=false, preview=false, &block)
  return dump_file(configs, path, recurse, preview, &DEFAULT_DUMP) unless block_given?
  
  current = ""
  dumps = [[path, current]]
  
  dump(configs, current) do |key, config|
    if recurse && config.kind_of?(NestConfig)
      dumps.concat(dump_file(config.nest_class.configurations, recursive_path(key, path), true, true, &block))
      ""
    else
      yield(key, config)
    end
  end
  
  dumps.each do |dump_path, content|
    dir = File.dirname(dump_path)
    Dir.mkdir(dir) unless File.exists?(dir)
    File.open(dump_path, "w") do |io|
      io << content
    end 
  end unless preview
  
  dumps
end

.load(str) ⇒ Object

Loads the string as YAML.



126
127
128
# File 'lib/configurable/utils.rb', line 126

def load(str)
  YAML.load(str)
end

.load_file(path, recurse = false, &block) ⇒ Object

Loads the file contents as YAML. If recurse is true, a hash will be recursively loaded. A block may be provided to set recursively loaded values in the hash loaded from the path.



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
# File 'lib/configurable/utils.rb', line 133

def load_file(path, recurse=false, &block)
  return load_file(path, recurse, &DEFAULT_LOAD) if recurse && !block_given?
  base = File.directory?(path) ? {} : (YAML.load_file(path) || {})
  
  if recurse
    # determine the files/dirs to load recursively
    # and add them to paths by key (ie the base
    # name of the path, minus any extname)
    paths = {}
    files, dirs = Dir.glob("#{path.chomp(File.extname(path))}/*").partition do |sub_path|
      File.file?(sub_path)
    end

    # directories are added to paths first so they can be
    # overridden by the files (appropriate since the file
    # will recursively load the directory if it exists)
    dirs.each do |dir|
      paths[File.basename(dir)] = dir
    end

    # when adding files, check that no two files map to
    # the same key (ex a.yml, a.yaml).
    files.each do |filepath|
      key = File.basename(filepath).chomp(File.extname(filepath))
      if existing = paths[key]
        if File.file?(existing)
          confict = [File.basename(paths[key]), File.basename(filepath)].sort
          raise "multiple files load the same key: #{confict.inspect}"
        end
      end

      paths[key] = filepath
    end

    # recursively load each file and reverse merge
    # the result into the base
    paths.each_pair do |key, recursive_path|
      value = load_file(recursive_path, true, &block)
      yield(base, key, value)
    end
  end

  base
end

.recursive_path(key, path) ⇒ Object

A helper to create and prepare a recursive dump path.



179
180
181
182
183
184
# File 'lib/configurable/utils.rb', line 179

def recursive_path(key, path)
  ext = File.extname(path)
  dir = path.chomp(ext)
  
  "#{File.join(dir, key.to_s)}#{ext}"
end