app_config 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +39 -0
- data/Rakefile +77 -0
- data/lib/app_config.rb +30 -0
- data/lib/app_config/base.rb +51 -0
- data/lib/app_config/storage.rb +5 -0
- data/lib/app_config/storage/yaml.rb +23 -0
- data/lib/core_ext/hashish.rb +124 -0
- data/spec/app_config/base_spec.rb +20 -0
- data/spec/app_config/storage/yaml_spec.rb +17 -0
- data/spec/app_config_spec.rb +7 -0
- data/spec/core_ext/hashish_spec.rb +18 -0
- data/spec/fixtures/app_config.yml +4 -0
- data/spec/rcov.opts +1 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +17 -0
- metadata +72 -0
data/README
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
= AppConfig
|
2
|
+
|
3
|
+
An easy to use, customizable library to easily store and retrieve application
|
4
|
+
configuration, API keys or basically anything in 'key => value' pairs.
|
5
|
+
|
6
|
+
|
7
|
+
== Usage
|
8
|
+
|
9
|
+
Usage is simple. Just pass either a hash of options, or a block, to
|
10
|
+
AppConfig.configure. See AppConfig::Base for a list of valid options.
|
11
|
+
|
12
|
+
For example, given this YAML file:
|
13
|
+
|
14
|
+
---
|
15
|
+
admin_email: 'admin@example.com'
|
16
|
+
api_name: 'Supr Webz 2.0'
|
17
|
+
api_key: 'SUPERAWESOMESERVICE'
|
18
|
+
|
19
|
+
Use it like so:
|
20
|
+
|
21
|
+
require 'app_config'
|
22
|
+
|
23
|
+
AppConfig.configure do |config|
|
24
|
+
config.storage_method = :yaml
|
25
|
+
config.path = '/path/to/app_config.yml'
|
26
|
+
end
|
27
|
+
|
28
|
+
AppConfig[admin_email] # => 'admin@example.com'
|
29
|
+
AppConfig[:api_name] # => 'Supr Webz 2.0'
|
30
|
+
AppConfig[:api_key] # => 'SUPERAWESOMESERVICE'
|
31
|
+
|
32
|
+
Want SQLite3 support? No problem!
|
33
|
+
|
34
|
+
AppConfig.configure do |config|
|
35
|
+
config.storage_method = :sqlite
|
36
|
+
config.database = '/path/to/database.sqlite3'
|
37
|
+
end
|
38
|
+
|
39
|
+
AppConfig[:column] # => 'value'
|
data/Rakefile
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec/rake/spectask'
|
2
|
+
|
3
|
+
task :default => [:spec]
|
4
|
+
|
5
|
+
desc 'Start an irb session with AppConfig loaded'
|
6
|
+
task :console do
|
7
|
+
sh "irb -I ./lib -r 'app_config'"
|
8
|
+
end
|
9
|
+
|
10
|
+
desc 'Run the specs with autotest'
|
11
|
+
task :autotest do
|
12
|
+
ENV['RSPEC'] = 'true'
|
13
|
+
sh 'autotest'
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Run all specs in spec directory'
|
17
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
18
|
+
t.spec_opts = ['--options', 'spec/spec.opts']
|
19
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'Run all specs in spec directory with RCov'
|
23
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
24
|
+
t.spec_opts = ['--options', 'spec/spec.opts']
|
25
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
26
|
+
t.rcov = true
|
27
|
+
t.rcov_opts = lambda do
|
28
|
+
IO.readlines('spec/rcov.opts').map {|l| l.chomp.split ' '}.flatten
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
desc 'Generate API documentation'
|
33
|
+
task :doc do
|
34
|
+
Rake::Task['doc:api'].invoke
|
35
|
+
end
|
36
|
+
|
37
|
+
namespace :doc do
|
38
|
+
task :setup_rdoc do
|
39
|
+
@file_list = FileList[ "#{File.dirname(__FILE__)}/README",
|
40
|
+
"#{File.dirname(__FILE__)}/lib/**/*.rb" ]
|
41
|
+
# Substitute APP_ROOT with a dot. Makes for a better index in the generated docs.
|
42
|
+
@files = @file_list.collect {|f| f.gsub(/#{File.dirname(__FILE__)}/, '.')}
|
43
|
+
@options = %W[
|
44
|
+
--all
|
45
|
+
--inline-source
|
46
|
+
--line-numbers
|
47
|
+
--main README
|
48
|
+
--op #{File.join(File.dirname(__FILE__), 'doc', 'api')}
|
49
|
+
--title 'AppConfig API Documentation'
|
50
|
+
]
|
51
|
+
# Generate a diagram, yes/no?
|
52
|
+
@options << '-d' if RUBY_PLATFORM !~ /win32/ && `which dot` =~ /\/dot/ && !ENV['NODOT']
|
53
|
+
end
|
54
|
+
|
55
|
+
task :api => [:setup_rdoc] do
|
56
|
+
run_rdoc(@options, @files)
|
57
|
+
end
|
58
|
+
|
59
|
+
desc 'Remove generated API documentation'
|
60
|
+
task :clear do
|
61
|
+
system("rm -rf #{File.dirname(__FILE__) + '/doc/api'}")
|
62
|
+
end
|
63
|
+
|
64
|
+
desc 'Rebuild API documentation'
|
65
|
+
task :rebuild do
|
66
|
+
Rake::Task['doc:clear'].invoke
|
67
|
+
Rake::Task['doc:api'].invoke
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def run_rdoc(options, files)
|
74
|
+
options = options.join(' ') if options.is_a? Array
|
75
|
+
files = files.join(' ') if files.is_a? Array
|
76
|
+
system("rdoc #{options} #{files}")
|
77
|
+
end
|
data/lib/app_config.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
require 'core_ext/hashish'
|
6
|
+
|
7
|
+
module AppConfig
|
8
|
+
VERSION = '0.1.0'
|
9
|
+
|
10
|
+
autoload :Base, 'app_config/base'
|
11
|
+
autoload :Storage, 'app_config/storage'
|
12
|
+
|
13
|
+
def self.to_version
|
14
|
+
"#{self.class} v#{VERSION}"
|
15
|
+
end
|
16
|
+
|
17
|
+
# Access the configured <tt>key</tt>'s value.
|
18
|
+
def self.[](key)
|
19
|
+
error = "Must call '#{self}.configure' to setup storage!"
|
20
|
+
raise error if @@storage.nil?
|
21
|
+
@@storage[key]
|
22
|
+
end
|
23
|
+
|
24
|
+
# Accepts an +options+ hash or pass a block.
|
25
|
+
# See AppConfig::Base for valid options.
|
26
|
+
def self.configure(options = {}, &block)
|
27
|
+
@@storage = AppConfig::Base.new(options, &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module AppConfig
|
2
|
+
|
3
|
+
# The Base storage class.
|
4
|
+
# Acts as a wrapper for the different storage methods.
|
5
|
+
#
|
6
|
+
# See each storage method's documentation for their specific options.
|
7
|
+
class Base
|
8
|
+
|
9
|
+
attr_accessor :storage_method, :path
|
10
|
+
|
11
|
+
DEFAULTS = {
|
12
|
+
:storage_method => :yaml,
|
13
|
+
:path => File.expand_path(File.join(ENV['HOME'], '.app_config.yml'))
|
14
|
+
}
|
15
|
+
|
16
|
+
# Accepts either a hash of +options+ or a block (which overrides
|
17
|
+
# any options passed in the hash).
|
18
|
+
#
|
19
|
+
# Valid storage methods:
|
20
|
+
# * :sqlite
|
21
|
+
# * :yaml
|
22
|
+
def initialize(options = {}, &block)
|
23
|
+
DEFAULTS.merge(options).each_pair do |key, value|
|
24
|
+
self.send("#{ key }=", value)
|
25
|
+
end
|
26
|
+
yield self if block_given?
|
27
|
+
@storage = initialize_storage
|
28
|
+
end
|
29
|
+
|
30
|
+
# Access the <tt>key</tt>'s value in @storage.
|
31
|
+
def [](key)
|
32
|
+
@storage[key]
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# This decides how to load the data, based on the +storage_method+.
|
38
|
+
def initialize_storage
|
39
|
+
case storage_method
|
40
|
+
when :sqlite
|
41
|
+
# TODO: Initialize SQLite3 storage.
|
42
|
+
when :yaml
|
43
|
+
AppConfig::Storage::Yaml.load(path)
|
44
|
+
else
|
45
|
+
raise 'Unknown storage method.'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end # Base
|
50
|
+
|
51
|
+
end # AppConfig
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module AppConfig
|
2
|
+
module Storage
|
3
|
+
|
4
|
+
# YAML storage method.
|
5
|
+
class Yaml
|
6
|
+
attr_reader :data
|
7
|
+
|
8
|
+
# Loads @data with the YAML file located at +path+.
|
9
|
+
#
|
10
|
+
# Defaults to $HOME/.app_config.yml
|
11
|
+
def initialize(path)
|
12
|
+
@data = Hashish.new(YAML.load_file(path))
|
13
|
+
end
|
14
|
+
|
15
|
+
# Creates a new Yaml storage with the given +path+ and returns the data.
|
16
|
+
def self.load(path)
|
17
|
+
new(path).data
|
18
|
+
end
|
19
|
+
|
20
|
+
end # Yaml
|
21
|
+
|
22
|
+
end # Storage
|
23
|
+
end # AppConfig
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# Stolen from Rails Active Support 2.3.0 and renamed to Hashish.
|
2
|
+
#
|
3
|
+
# This class has dubious semantics and we only have it so that
|
4
|
+
# people can write params[:key] instead of params['key']
|
5
|
+
# and they get the same value for both keys.
|
6
|
+
class Hashish < Hash
|
7
|
+
def initialize(constructor = {})
|
8
|
+
if constructor.is_a?(Hash)
|
9
|
+
super()
|
10
|
+
update(constructor)
|
11
|
+
else
|
12
|
+
super(constructor)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def default(key = nil)
|
17
|
+
if key.is_a?(Symbol) && include?(key = key.to_s)
|
18
|
+
self[key]
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
|
25
|
+
alias_method :regular_update, :update unless method_defined?(:regular_update)
|
26
|
+
|
27
|
+
# Assigns a new value to the hash:
|
28
|
+
#
|
29
|
+
# hash = HashWithIndifferentAccess.new
|
30
|
+
# hash[:key] = "value"
|
31
|
+
#
|
32
|
+
def []=(key, value)
|
33
|
+
regular_writer(convert_key(key), convert_value(value))
|
34
|
+
end
|
35
|
+
|
36
|
+
# Updates the instantized hash with values from the second:
|
37
|
+
#
|
38
|
+
# hash_1 = HashWithIndifferentAccess.new
|
39
|
+
# hash_1[:key] = "value"
|
40
|
+
#
|
41
|
+
# hash_2 = HashWithIndifferentAccess.new
|
42
|
+
# hash_2[:key] = "New Value!"
|
43
|
+
#
|
44
|
+
# hash_1.update(hash_2) # => {"key"=>"New Value!"}
|
45
|
+
#
|
46
|
+
def update(other_hash)
|
47
|
+
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
alias_method :merge!, :update
|
52
|
+
|
53
|
+
# Checks the hash for a key matching the argument passed in:
|
54
|
+
#
|
55
|
+
# hash = HashWithIndifferentAccess.new
|
56
|
+
# hash["key"] = "value"
|
57
|
+
# hash.key? :key # => true
|
58
|
+
# hash.key? "key" # => true
|
59
|
+
#
|
60
|
+
def key?(key)
|
61
|
+
super(convert_key(key))
|
62
|
+
end
|
63
|
+
|
64
|
+
alias_method :include?, :key?
|
65
|
+
alias_method :has_key?, :key?
|
66
|
+
alias_method :member?, :key?
|
67
|
+
|
68
|
+
# Fetches the value for the specified key, same as doing hash[key]
|
69
|
+
def fetch(key, *extras)
|
70
|
+
super(convert_key(key), *extras)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns an array of the values at the specified indices:
|
74
|
+
#
|
75
|
+
# hash = HashWithIndifferentAccess.new
|
76
|
+
# hash[:a] = "x"
|
77
|
+
# hash[:b] = "y"
|
78
|
+
# hash.values_at("a", "b") # => ["x", "y"]
|
79
|
+
#
|
80
|
+
def values_at(*indices)
|
81
|
+
indices.collect {|key| self[convert_key(key)]}
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns an exact copy of the hash.
|
85
|
+
def dup
|
86
|
+
HashWithIndifferentAccess.new(self)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
|
90
|
+
# Does not overwrite the existing hash.
|
91
|
+
def merge(hash)
|
92
|
+
self.dup.update(hash)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Removes a specified key from the hash.
|
96
|
+
def delete(key)
|
97
|
+
super(convert_key(key))
|
98
|
+
end
|
99
|
+
|
100
|
+
def stringify_keys!; self end
|
101
|
+
def symbolize_keys!; self end
|
102
|
+
def to_options!; self end
|
103
|
+
|
104
|
+
# Convert to a Hash with String keys.
|
105
|
+
def to_hash
|
106
|
+
Hash.new(default).merge(self)
|
107
|
+
end
|
108
|
+
|
109
|
+
protected
|
110
|
+
def convert_key(key)
|
111
|
+
key.kind_of?(Symbol) ? key.to_s : key
|
112
|
+
end
|
113
|
+
|
114
|
+
def convert_value(value)
|
115
|
+
case value
|
116
|
+
when Hash
|
117
|
+
value.with_indifferent_access
|
118
|
+
when Array
|
119
|
+
value.collect { |e| e.is_a?(Hash) ? e.with_indifferent_access : e }
|
120
|
+
else
|
121
|
+
value
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Base do
|
4
|
+
|
5
|
+
it 'should raise error on unknown option' do
|
6
|
+
lambda do
|
7
|
+
Base.new(:unknown => 'option')
|
8
|
+
end.should raise_error(NoMethodError)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should have default options' do
|
12
|
+
default_path = File.expand_path(File.join(ENV['HOME'], '.app_config.yml'))
|
13
|
+
# mock up the YAML stuff, so it won't puke
|
14
|
+
YAML.should_receive(:load_file).with(default_path).and_return({:api => 'key'})
|
15
|
+
base = Base.new
|
16
|
+
base.storage_method.should == :yaml
|
17
|
+
base.path.should == default_path
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
include AppConfig::Storage
|
4
|
+
describe Yaml do
|
5
|
+
|
6
|
+
it 'should have some values' do
|
7
|
+
config_for_yaml
|
8
|
+
AppConfig[:api_key].should_not be_nil
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should raise file not found' do
|
12
|
+
lambda do
|
13
|
+
config_for_yaml(:path => 'not/a/real/file.yml')
|
14
|
+
end.should raise_error(Errno::ENOENT)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Hashish do
|
4
|
+
before(:each) do
|
5
|
+
@strings = { 'key' => 'value', 'four' => 20 }
|
6
|
+
@symbols = { :key => 'value', :four => 20 }
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should not give a fuck about symbols' do
|
10
|
+
hashish = Hashish.new(@strings)
|
11
|
+
hashish[:key].should == 'value'
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should not give a fuck about strings' do
|
15
|
+
hashish = Hashish.new(@symbols)
|
16
|
+
hashish['key'].should == 'value'
|
17
|
+
end
|
18
|
+
end
|
data/spec/rcov.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--exclude "spec/*,gems/*"
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec'
|
2
|
+
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/app_config')
|
4
|
+
|
5
|
+
Spec::Runner.configure do |config|
|
6
|
+
include AppConfig
|
7
|
+
end
|
8
|
+
|
9
|
+
FIXTURE = File.expand_path(File.dirname(__FILE__) + '/fixtures/app_config.yml')
|
10
|
+
def config_for_yaml(opts = {})
|
11
|
+
path = opts[:path] || FIXTURE
|
12
|
+
@yaml = {
|
13
|
+
:storage_method => :yaml,
|
14
|
+
:path => path,
|
15
|
+
}
|
16
|
+
AppConfig.configure(@yaml.merge(opts))
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: app_config
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dale Campbell
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-13 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: An easy to use, customizable library to easily store and retrieve application configuration
|
17
|
+
email:
|
18
|
+
- dale@save-state.net
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files: []
|
24
|
+
|
25
|
+
files:
|
26
|
+
- README
|
27
|
+
- Rakefile
|
28
|
+
- lib/app_config/base.rb
|
29
|
+
- lib/app_config/storage/yaml.rb
|
30
|
+
- lib/app_config/storage.rb
|
31
|
+
- lib/app_config.rb
|
32
|
+
- lib/core_ext/hashish.rb
|
33
|
+
- spec/app_config
|
34
|
+
- spec/app_config/base_spec.rb
|
35
|
+
- spec/app_config/storage
|
36
|
+
- spec/app_config/storage/yaml_spec.rb
|
37
|
+
- spec/app_config_spec.rb
|
38
|
+
- spec/core_ext
|
39
|
+
- spec/core_ext/hashish_spec.rb
|
40
|
+
- spec/fixtures
|
41
|
+
- spec/fixtures/app_config.yml
|
42
|
+
- spec/rcov.opts
|
43
|
+
- spec/spec.opts
|
44
|
+
- spec/spec_helper.rb
|
45
|
+
has_rdoc: false
|
46
|
+
homepage: http://oshuma.github.com/app_config
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
version:
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 1.3.1
|
68
|
+
signing_key:
|
69
|
+
specification_version: 2
|
70
|
+
summary: Quick and easy application configuration.
|
71
|
+
test_files: []
|
72
|
+
|