Using YAML as a parameter hash in Ruby

Sometimes while coding Ruby applications the need develops to have some sort of metadata available as a configuration file. I have written some Ruby code that allows parameters that exist in a yaml file to be used as though they are in the class itself. In addition, I required code that allows the class to modify these parameters and then save them back to the file. Ruby's method_missing is a major player here. Anyway, I thought I would share this code.

To give an example of what exactly this does, assume that there exists a file called 'config.yml' that contains some key value pair such as :name => 'Bob':

  1. ---
  2. :name: Bob

Using this class, we could do stuff like:

  1. require 'boilerplate'
  2.  
  3. class Test < Boilerplate
  4. def run
  5. puts "Name is: #{name}"
  6. super
  7. end
  8. end
  9.  
  10. t = Test.new
  11. puts t.name #=> 'Bob'
  12. t.name = 'Joe'
  13. puts t.name #=> 'Joe'
  14. t.run #=> 'Name is: Joe'

Which would result in the yaml file now having the key value pair of :name => 'Joe'. If you didn't want to call 'run' you could call flush manually or add it as a call to some other method.

Here is the boilerplate.rb code in full:

  1. class Boilerplate
  2. require 'yaml'
  3. require 'ftools'
  4.  
  5. CACHE_LOCATION = File.join('.')
  6. CACHE_FILE = 'config.yml'
  7. CACHE_PATH = File.join(File.expand_path(CACHE_LOCATION), CACHE_FILE)
  8.  
  9. def initialize
  10. ensure_cache_exists
  11. load_settings
  12. end
  13.  
  14. def run
  15. # do stuff here
  16. flush
  17. end
  18.  
  19. private
  20.  
  21. def method_missing(symbol, *args, &block)
  22. string = symbol.to_s # need for '=' method
  23.  
  24. if @options.key?(symbol)
  25. @options[symbol]
  26. elsif string.index('=') + 1 == string.length && @options.key?(string.gsub('=', '').intern)
  27. @options[string.gsub('=', '').intern] = *args
  28. else
  29. super(symbol, *args, &block)
  30. end
  31. end
  32.  
  33. def load_settings
  34. @options = {}
  35. imports = YAML.load_file(CACHE_PATH)
  36. if imports
  37. imports.each_pair do |key, value|
  38. if not key.is_a? Symbol
  39. imports[key.to_sym] = value
  40. imports.delete(key)
  41. end
  42. end
  43.  
  44. @options = imports.merge(@options)
  45. end
  46. end
  47.  
  48. def ensure_cache_exists
  49. File.makedirs(CACHE_LOCATION)
  50. File.new(CACHE_PATH, 'a+') unless File.exists?(CACHE_PATH)
  51. end
  52.  
  53. def flush
  54. File.open(CACHE_PATH, 'w') do |f|
  55. f.puts(YAML::dump(@options))
  56. end
  57. end
  58. end