Skip to content
This repository has been archived by the owner on Apr 5, 2018. It is now read-only.

Commit

Permalink
Add a simple configuration system that uses sysctl(8) variable format.
Browse files Browse the repository at this point in the history
  • Loading branch information
mheffner committed Sep 30, 2011
1 parent b30e8f7 commit 4a35763
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 9 deletions.
7 changes: 5 additions & 2 deletions lib/twke.rb
@@ -1,8 +1,11 @@
module Twke
require 'scamp'

require 'twke/routes.rb'
require 'twke/plugin.rb'
$:.unshift File.join(File.dirname(__FILE__), 'twke')

require 'conf'
require 'routes'
require 'plugin'

def self.version
File.read(File.join(File.dirname(__FILE__), '../VERSION')).chomp
Expand Down
122 changes: 122 additions & 0 deletions lib/twke/conf.rb
@@ -0,0 +1,122 @@
require 'yaml'

#
# Simple configuration system.
#
# Variable names are dot-separated, similar to how sysctl(8) names
# work.
#
# For example, a 'heroku' module could prefix all variables with
# "heroku.":
#
# heroku.sites.metrics-prod.giturl
# heroku.sites.metrics-prod.token
#
# The conf system can also lookup all the sub-variables rooted at a
# single prefix. So for example, assume the following variables are
# also set:
#
# heroku.sites.metrics-stg.giturl
# heroku.sites.metrics-stg.token
#
# A list command on 'heroku.sites' would return:
# ['metrics-prod', 'metrics-stg']
#
######

module Twke
module Conf
class << self
attr_accessor :conf

def conf
@conf ||= {}
end

# Everytime a value is modified the config DB is written
def set(varname, value)
if varname =~ /^\./ || varname =~ /\.$/ || varname.length == 0
raise "Invalid variable name"
end

conf[varname.to_s] = value

save
end

def get(varname)
conf[varname.to_s]
end

def exists?(varname)
conf.has_key?(varname)
end

#
# This will return a list of the unique commands that begin with
# the varpfx prefix assumed to be a sub-command prefix.
#
# For example, assume the following variables are set:
#
# net.tcp.foobar => 1
# net.tcp.barbar => 2
#
# So:
# list('net.tcp') would return: ["barbar", "foobar"]
# list('net') would return: ["tcp"]
#
# list('net.tc') would return: []
# Because there are no sub-commands with 'net.tc.' as
# their prefix
#
def list(varpfx)
# Strip leading/trailing periods
varpfx = varpfx[1, varpfx.length] if varpfx =~ /^\./
varpfx = varpfx[0, varpfx.length - 1] if varpfx =~ /\.$/

# XXX: Really need a tree structure to do this efficiently
conf.keys.inject([]) do |ar, k|
if k =~ /^#{varpfx}\./
ar << k.gsub(/^#{varpfx}\./, '').split(".")[0]
end
ar
end.sort.uniq
end

def load
begin
yml = YAML.load_file(config_file)
@conf = yml
rescue Errno::ENOENT => err
@conf = {}
rescue => err
raise "Unknown error reading config file: #{err.message}"
end
end

#
# Files are saved atomically.
#
def save
unless File.exist?(config_dir)
FileUtils.mkdir_p(config_dir, :mode => 0700)
end

tmpfile = File.join(config_dir, "tmpconfig_#{rand 999999}")
File.open(tmpfile, "w") do |f|
YAML.dump(conf, f )
end

FileUtils.mv(tmpfile, config_file)
end

def config_dir
File.join(ENV['HOME'], '.twke')
end

def config_file
File.join(config_dir, 'config.yml')
end
end
end
end
1 change: 1 addition & 0 deletions test/helper.rb
Expand Up @@ -15,4 +15,5 @@
require 'twke'

class Test::Unit::TestCase
include Twke
end
51 changes: 51 additions & 0 deletions test/test_conf.rb
@@ -0,0 +1,51 @@
require 'helper'

class TestConf < Test::Unit::TestCase
def setup
FileUtils.rm_rf(Conf::config_file)
end

def teardown
FileUtils.rm_rf(Conf::config_file)
end

def validate_settings
assert_equal(1, Conf::get('net.tcp.foobar'))
assert_equal('blah', Conf::get('net.tcp.barbar'))
assert(Conf::exists?('net.tcp.foobar'))
assert(Conf::exists?('net.tcp.barbar'))
assert(!Conf::exists?('net.tcp'))

assert_equal(['barbar', 'foobar'].sort, Conf::list('net.tcp'))
assert_equal(['tcp'].sort, Conf::list('net'))

assert_equal([], Conf::list(''))
assert_equal([], Conf::list('net.tc'))
end

def test_config
Conf::set('net.tcp.foobar', 1)
Conf::set('net.tcp.barbar', "blah")

validate_settings

['.sys.blah', 'sys.blah.', ''].each do |bad|
begin
Conf::set(bad, rand(5))
rescue
# Should throw exception
else
assert(false, "Did not throw exception setting: #{bad}")
end
end

assert(File.exist?(Conf::config_file), "Conf file doesn't exist")

# Now reload
#
Conf::load

# retest after reload
validate_settings
end
end
7 changes: 0 additions & 7 deletions test/test_twke.rb

This file was deleted.

0 comments on commit 4a35763

Please sign in to comment.