app_config 0.2.3 → 0.2.4
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 +48 -0
- data/Rakefile +10 -0
- data/lib/app_config/base.rb +61 -0
- data/lib/app_config/storage/sqlite.rb +59 -0
- data/lib/app_config/storage/yaml.rb +29 -0
- data/lib/app_config/storage.rb +6 -0
- data/lib/app_config.rb +44 -0
- data/lib/core_ext/hashish.rb +137 -0
- metadata +24 -7
data/README
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
= AppConfig
|
2
|
+
|
3
|
+
An easy to use, customizable library to easily store and retrieve application
|
4
|
+
(or library) 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.setup. See AppConfig::Base for a list of valid storage methods.
|
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.setup do |config|
|
24
|
+
config[:storage_method] = :yaml
|
25
|
+
config[:path] = '/path/to/app_config.yml'
|
26
|
+
end
|
27
|
+
|
28
|
+
# Later on...
|
29
|
+
# Strings or symbols as keys.
|
30
|
+
AppConfig['admin_email'] # => 'admin@example.com'
|
31
|
+
AppConfig[:api_name] # => 'Supr Webz 2.0'
|
32
|
+
AppConfig[:api_key] # => 'SUPERAWESOMESERVICE'
|
33
|
+
|
34
|
+
Want SQLite3? No problem!
|
35
|
+
|
36
|
+
AppConfig.setup do |config|
|
37
|
+
config[:storage_method] = :sqlite
|
38
|
+
config[:database] = '/path/to/database.sqlite3'
|
39
|
+
config[:table] = 'app_config'
|
40
|
+
end
|
41
|
+
|
42
|
+
AppConfig[:column] # => 'value'
|
43
|
+
|
44
|
+
You can also pass a <tt>:uri</tt> option, which will automatically determine
|
45
|
+
the options for the storage method. So in the block (or passed in hash):
|
46
|
+
|
47
|
+
config[:uri] = 'sqlite://path/to/database.sqlite3'
|
48
|
+
config[:uri] = 'yaml://path/to/app_config.yml'
|
data/Rakefile
ADDED
@@ -0,0 +1,61 @@
|
|
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
|
+
#
|
8
|
+
# Valid storage methods:
|
9
|
+
# * :sqlite (AppConfig::Storage::Sqlite)
|
10
|
+
# * :yaml (AppConfig::Storage::YAML)
|
11
|
+
class Base
|
12
|
+
|
13
|
+
# TODO: Change the default storage method to not use YAML.
|
14
|
+
DEFAULTS = {
|
15
|
+
:storage_method => :yaml,
|
16
|
+
}
|
17
|
+
|
18
|
+
# Accepts either a hash of +options+ or a block (which overrides
|
19
|
+
# any options passed in the hash).
|
20
|
+
def initialize(options = {}, &block)
|
21
|
+
@options = DEFAULTS.merge(options)
|
22
|
+
yield @options if block_given?
|
23
|
+
determine_storage_method if @options[:uri]
|
24
|
+
@storage = initialize_storage
|
25
|
+
end
|
26
|
+
|
27
|
+
# Access the <tt>key</tt>'s value in @storage.
|
28
|
+
def [](key)
|
29
|
+
@storage[key]
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# Sets the storage_method depending on the URI given.
|
35
|
+
def determine_storage_method
|
36
|
+
uri = URI.parse(@options.delete(:uri))
|
37
|
+
case uri.scheme
|
38
|
+
when 'sqlite'
|
39
|
+
@options[:storage_method] = :sqlite
|
40
|
+
@options[:database] = uri.path
|
41
|
+
when 'yaml'
|
42
|
+
@options[:storage_method] = :yaml
|
43
|
+
@options[:path] = uri.path
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# This decides how to load the data, based on the +storage_method+.
|
48
|
+
# TODO: Maybe purge AppConfig options (ie, those not related to the user-end).
|
49
|
+
def initialize_storage
|
50
|
+
case @options[:storage_method]
|
51
|
+
when :sqlite
|
52
|
+
AppConfig::Storage::Sqlite.load(@options)
|
53
|
+
when :yaml
|
54
|
+
AppConfig::Storage::YAML.load(@options)
|
55
|
+
else
|
56
|
+
raise UnknownStorageMethod
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end # Base
|
61
|
+
end # AppConfig
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module AppConfig
|
2
|
+
module Storage
|
3
|
+
|
4
|
+
# SQLite3 storage method.
|
5
|
+
class Sqlite
|
6
|
+
attr_accessor :data
|
7
|
+
|
8
|
+
DEFAULTS = {
|
9
|
+
:database => File.expand_path(File.join(ENV['HOME'], '.app_config.sqlite3')),
|
10
|
+
:table => 'app_config'
|
11
|
+
}
|
12
|
+
|
13
|
+
# Loads @data with the SQLite3 database located at +path+.
|
14
|
+
# @data will be the Hashish that is accessed like AppConfig[:key].
|
15
|
+
#
|
16
|
+
# Defaults to $HOME/.app_config.sqlite3
|
17
|
+
def initialize(options)
|
18
|
+
@options = DEFAULTS.merge(options)
|
19
|
+
@db = SQLite3::Database.open(@options[:database])
|
20
|
+
@data = load_from_database
|
21
|
+
end
|
22
|
+
|
23
|
+
# Creates a new Sqlite storage with the given +path+ and returns the data.
|
24
|
+
def self.load(path)
|
25
|
+
new(path).data
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# Returns a Hashish that looks something like {:column => value}.
|
31
|
+
# TODO: This could use a bit of work.
|
32
|
+
def load_from_database
|
33
|
+
query = ["SELECT * FROM sqlite_master",
|
34
|
+
"WHERE type == 'table' AND name == '#{@options[:table]}'",
|
35
|
+
"AND name != 'sqlite_sequence'"].join(' ')
|
36
|
+
table = @db.get_first_row(query).last
|
37
|
+
values(columns(table))
|
38
|
+
end
|
39
|
+
|
40
|
+
def values(columns)
|
41
|
+
data = Hashish.new
|
42
|
+
query = "SELECT #{columns.join(', ')} FROM #{@options[:table]}"
|
43
|
+
@db.get_first_row(query).each_with_index do |value, index|
|
44
|
+
data[columns[index]] = value
|
45
|
+
end
|
46
|
+
data
|
47
|
+
end
|
48
|
+
|
49
|
+
def columns(table)
|
50
|
+
columns = table.split(', ')
|
51
|
+
# Trip the first element, since it's the SQL CREATE statement.
|
52
|
+
columns = columns[1, columns.size]
|
53
|
+
# Yes, Ruby is 'elegant', but this is fucking disgusting. There *must* be a better way.
|
54
|
+
columns.map! {|c| c.split('"').reject {|e| e.empty? }.reject {|e| e.include? '('}}.flatten!
|
55
|
+
end
|
56
|
+
|
57
|
+
end # Sqlite
|
58
|
+
end # Storage
|
59
|
+
end # AppConfig
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module AppConfig
|
2
|
+
module Storage
|
3
|
+
|
4
|
+
# YAML storage method.
|
5
|
+
class YAML
|
6
|
+
attr_reader :data
|
7
|
+
|
8
|
+
DEFAULTS = {
|
9
|
+
:path => File.expand_path(File.join(ENV['HOME'], '.app_config.yml'))
|
10
|
+
}
|
11
|
+
|
12
|
+
# Loads @data with the YAML file located at +path+.
|
13
|
+
# @data will be the Hashish that is accessed with AppConfig[:key].
|
14
|
+
#
|
15
|
+
# Defaults to $HOME/.app_config.yml
|
16
|
+
def initialize(options)
|
17
|
+
path = options[:path] || DEFAULTS[:path]
|
18
|
+
# Make sure to use the top-level YAML module here.
|
19
|
+
@data = Hashish.new(::YAML.load_file(path))
|
20
|
+
end
|
21
|
+
|
22
|
+
# Creates a new YAML storage with the given +path+ and returns the data.
|
23
|
+
def self.load(path)
|
24
|
+
new(path).data
|
25
|
+
end
|
26
|
+
|
27
|
+
end # YAML
|
28
|
+
end # Storage
|
29
|
+
end # AppConfig
|
data/lib/app_config.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__)
|
2
|
+
|
3
|
+
# TODO: Only load deps if needed (no gems unless required).
|
4
|
+
libs = %w{ sqlite3 yaml uri }
|
5
|
+
|
6
|
+
begin
|
7
|
+
libs.each { |lib| require lib }
|
8
|
+
rescue LoadError
|
9
|
+
require 'rubygems'
|
10
|
+
libs.each { |lib| require lib }
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'core_ext/hashish'
|
14
|
+
|
15
|
+
# TODO: Move these to their own file.
|
16
|
+
class NotConfigured < Exception
|
17
|
+
def to_s; "Must call 'AppConfig.setup' to setup storage!"; end
|
18
|
+
end
|
19
|
+
class UnknownStorageMethod < Exception; end
|
20
|
+
|
21
|
+
module AppConfig
|
22
|
+
VERSION = '0.2.4'
|
23
|
+
|
24
|
+
autoload :Base, 'app_config/base'
|
25
|
+
autoload :Storage, 'app_config/storage'
|
26
|
+
|
27
|
+
# Returns the AppConfig version string.
|
28
|
+
def self.to_version
|
29
|
+
"#{self.class} v#{VERSION}"
|
30
|
+
end
|
31
|
+
|
32
|
+
# Access the configured <tt>key</tt>'s value.
|
33
|
+
def self.[](key)
|
34
|
+
raise NotConfigured unless defined?(@@storage)
|
35
|
+
@@storage[key]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Accepts an +options+ hash or a block.
|
39
|
+
# See AppConfig::Base for valid storage methods.
|
40
|
+
def self.setup(options = {}, &block)
|
41
|
+
@@storage = AppConfig::Base.new(options, &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
end # AppConfig
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# Stolen from Rails Active Support and aliased 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 HashWithIndifferentAccess < 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
|
+
# Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
|
96
|
+
# This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess.
|
97
|
+
def reverse_merge(other_hash)
|
98
|
+
super other_hash.with_indifferent_access
|
99
|
+
end
|
100
|
+
|
101
|
+
def reverse_merge!(other_hash)
|
102
|
+
replace(reverse_merge( other_hash ))
|
103
|
+
end
|
104
|
+
|
105
|
+
# Removes a specified key from the hash.
|
106
|
+
def delete(key)
|
107
|
+
super(convert_key(key))
|
108
|
+
end
|
109
|
+
|
110
|
+
def stringify_keys!; self end
|
111
|
+
def symbolize_keys!; self end
|
112
|
+
def to_options!; self end
|
113
|
+
|
114
|
+
# Convert to a Hash with String keys.
|
115
|
+
def to_hash
|
116
|
+
Hash.new(default).merge!(self)
|
117
|
+
end
|
118
|
+
|
119
|
+
protected
|
120
|
+
def convert_key(key)
|
121
|
+
key.kind_of?(Symbol) ? key.to_s : key
|
122
|
+
end
|
123
|
+
|
124
|
+
def convert_value(value)
|
125
|
+
case value
|
126
|
+
when Hash
|
127
|
+
value.with_indifferent_access
|
128
|
+
when Array
|
129
|
+
value.collect { |e| e.is_a?(Hash) ? e.with_indifferent_access : e }
|
130
|
+
else
|
131
|
+
value
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Simple alias.
|
137
|
+
Hashish = HashWithIndifferentAccess
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: app_config
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dale Campbell
|
@@ -11,8 +11,17 @@ cert_chain: []
|
|
11
11
|
|
12
12
|
date: 2009-09-27 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: sqlite3-ruby
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
16
25
|
description: An easy to use, customizable library to easily store and retrieve application configuration.
|
17
26
|
email:
|
18
27
|
- oshuma@gmail.com
|
@@ -22,15 +31,23 @@ extensions: []
|
|
22
31
|
|
23
32
|
extra_rdoc_files: []
|
24
33
|
|
25
|
-
files:
|
26
|
-
|
34
|
+
files:
|
35
|
+
- Rakefile
|
36
|
+
- README
|
37
|
+
- lib/app_config.rb
|
38
|
+
- lib/app_config/base.rb
|
39
|
+
- lib/app_config/storage.rb
|
40
|
+
- lib/app_config/storage/sqlite.rb
|
41
|
+
- lib/app_config/storage/yaml.rb
|
42
|
+
- lib/core_ext/hashish.rb
|
27
43
|
has_rdoc: true
|
28
44
|
homepage: http://oshuma.github.com/app_config
|
29
45
|
licenses: []
|
30
46
|
|
31
47
|
post_install_message:
|
32
|
-
rdoc_options:
|
33
|
-
|
48
|
+
rdoc_options:
|
49
|
+
- --inline-source
|
50
|
+
- --charset=UTF-8
|
34
51
|
require_paths:
|
35
52
|
- lib
|
36
53
|
required_ruby_version: !ruby/object:Gem::Requirement
|