construct 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/LICENSE +25 -0
- data/README +78 -0
- data/lib/construct.rb +135 -0
- metadata +55 -0
data/LICENSE
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Copyright (c) 2009 Kyle Kingsbury <aphyr@aphyr.com>
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice,
|
8
|
+
this list of conditions and the following disclaimer.
|
9
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
11
|
+
and/or other materials provided with the distribution.
|
12
|
+
* Neither the name of this project nor the names of its contributors may be
|
13
|
+
used to endorse or promote products derived from this software without
|
14
|
+
specific prior written permission.
|
15
|
+
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
17
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
18
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
19
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
20
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
21
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
22
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
23
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
24
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
25
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
Construct is extensible, persistent, structured configuration for Ruby and
|
2
|
+
humans with text editors. What do I mean?
|
3
|
+
|
4
|
+
You have a ruby program. Maybe it's a Ramaze app. It needs to store some
|
5
|
+
configuration data. It should be easily serialized to YAML so anyone can edit
|
6
|
+
the file easily. It should support nested structures. It should minimize
|
7
|
+
typing, and make access unambiguous. It should take no effort to create and
|
8
|
+
extend multi-layer configuration with many data types and accessors. Moreover,
|
9
|
+
it should allow you to define a schema for the options that are available,
|
10
|
+
making documentation easy. That's where Construct comes in.
|
11
|
+
|
12
|
+
gem install construct
|
13
|
+
|
14
|
+
config = Construct.new
|
15
|
+
|
16
|
+
Hey, we have a configuration object. Let's describe a web site. Keys are always symbols, but can be accessed via methods just like an OpenStruct.
|
17
|
+
|
18
|
+
config.name = "Aphyr"
|
19
|
+
config.base_url = "http://aphyr.com"
|
20
|
+
|
21
|
+
And getting at them is easy, too.
|
22
|
+
|
23
|
+
config.name
|
24
|
+
=> "Aphyr"
|
25
|
+
|
26
|
+
If the key you want is already a method, just use config#[] and config#[]=. Construct converts strings to symbols for you..
|
27
|
+
|
28
|
+
How about a database connection? Those parameters all belong together.
|
29
|
+
|
30
|
+
config.db = {
|
31
|
+
:user => 'cr',
|
32
|
+
:password => 'some password',
|
33
|
+
}
|
34
|
+
|
35
|
+
Secretly, Construct converted that hash into another Construct. You can chain
|
36
|
+
methods to easily access nested options.
|
37
|
+
|
38
|
+
config.db.user
|
39
|
+
=> 'cr'
|
40
|
+
|
41
|
+
Databases need a host. Let's define a host field.
|
42
|
+
|
43
|
+
config.db.schema[:host] = {
|
44
|
+
:default => '127.0.0.1',
|
45
|
+
:desc => 'The host the database adapter connects to.'
|
46
|
+
}
|
47
|
+
config.db.host
|
48
|
+
=> '127.0.0.1'
|
49
|
+
|
50
|
+
You can override this, naturally.
|
51
|
+
|
52
|
+
config.db.host = 'db.aphyr.com'
|
53
|
+
config.db.host
|
54
|
+
=> 'db.aphyr.com'
|
55
|
+
|
56
|
+
And it serializes neatly to YAML. Keys are expressed as strings, to save typing and make things look clean.
|
57
|
+
|
58
|
+
config.to_yaml =>
|
59
|
+
---
|
60
|
+
db:
|
61
|
+
user: cr
|
62
|
+
password: some password
|
63
|
+
name: Aphyr
|
64
|
+
base_url: http://aphyr.com
|
65
|
+
|
66
|
+
Hey, this is easy! Now let's load that configuration from a string.
|
67
|
+
|
68
|
+
config = Construct.load yaml
|
69
|
+
|
70
|
+
Maybe you need to implement extra logic in your config--perhaps you can specify either a db hash as shown above, or a connection string. Just subclass Construct::Construct or define methods directly on the construct.
|
71
|
+
|
72
|
+
class DBConfig < Construct::Construct
|
73
|
+
def db_str
|
74
|
+
self[:db_str] || make_db_string_from_hash(db)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
Problem solved. Now get back to having fun instead of worrying about configuration!
|
data/lib/construct.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
class Construct
|
2
|
+
# Construct is extensible, persistent, structured configuration for
|
3
|
+
# Ruby and humans with text editors.
|
4
|
+
|
5
|
+
APP_NAME = 'Construct'
|
6
|
+
APP_VERSION = '0.1.0'
|
7
|
+
APP_AUTHOR = 'Kyle Kingsbury'
|
8
|
+
APP_EMAIL = 'aphyr@aphyr.com'
|
9
|
+
APP_URL = 'http://aphyr.com'
|
10
|
+
APP_COPYRIGHT = 'Copyright (c) 2009 Kyle Kingsbury <aphyr@aphyr.com>. All rights reserved.'
|
11
|
+
|
12
|
+
require 'yaml'
|
13
|
+
|
14
|
+
YAML::add_domain_type("aphyr.com,2009", "construct") do |type, val|
|
15
|
+
# Not implemented ;)
|
16
|
+
Construct.load(val)
|
17
|
+
end
|
18
|
+
yaml_as "tag:aphyr.com,2009:construct"
|
19
|
+
yaml_as "tag:yaml.org,2002:map"
|
20
|
+
|
21
|
+
# Load a construct from a YAML string
|
22
|
+
def self.load(yaml)
|
23
|
+
hash = YAML::load(yaml)
|
24
|
+
new(hash)
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_accessor :schema, :data
|
28
|
+
|
29
|
+
def initialize(data = {}, schema = {})
|
30
|
+
@data = Hash.new
|
31
|
+
data.each do |key, value|
|
32
|
+
self[key] = value
|
33
|
+
end
|
34
|
+
@schema = schema
|
35
|
+
end
|
36
|
+
|
37
|
+
def ==(other)
|
38
|
+
@schema == other.schema and @data == other.data
|
39
|
+
end
|
40
|
+
|
41
|
+
def [](key)
|
42
|
+
key = key.to_sym if String === key
|
43
|
+
|
44
|
+
if @data.include? key
|
45
|
+
@data[key]
|
46
|
+
elsif @schema.include? key
|
47
|
+
@schema[key][:default]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Assign a value to a key. Constructs accept only symbols as values,
|
52
|
+
# and will convert strings to symbols when necessary. They will also
|
53
|
+
# implicitly convert Hashes as values into Constructs when possible. Hence
|
54
|
+
# you can do:
|
55
|
+
#
|
56
|
+
# construct.people = {:mary => 'Awesome', :joe => 'suspicious'}
|
57
|
+
# construct.people.mary # => 'Awesome'
|
58
|
+
def []=(key, value)
|
59
|
+
key = key.to_sym if String === key
|
60
|
+
raise ArgumentError.new('construct only accepts symbols (and strings) as keys.') unless key.is_a? Symbol
|
61
|
+
|
62
|
+
# Convert suitable hashes into Constructs
|
63
|
+
if value.is_a? Hash
|
64
|
+
if value.keys.all? { |k|
|
65
|
+
k.is_a? String or k.is_a? Symbol
|
66
|
+
}
|
67
|
+
value = Construct.new(value)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
@data[key] = value
|
72
|
+
end
|
73
|
+
|
74
|
+
# Clears the data in the construct.
|
75
|
+
def clear
|
76
|
+
@data.clear
|
77
|
+
end
|
78
|
+
|
79
|
+
# Defines a new field in the schema. Fields are :default and :desc.
|
80
|
+
def define(key, options = {})
|
81
|
+
key = key.to_sym if String === key
|
82
|
+
@schema[key] = options
|
83
|
+
end
|
84
|
+
|
85
|
+
# delete simply removes the value from the data hash, but leaves the schema
|
86
|
+
# unchanged. Hence the construct may still respond to include? if the
|
87
|
+
# schema defines that field. Use #schema.delete(:key) to remove the key
|
88
|
+
# entirely.
|
89
|
+
def delete(key)
|
90
|
+
key = key.to_sym if String === key
|
91
|
+
@data.delete key
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns true if the construct has a value set for, or the schema defines,
|
95
|
+
# the key.
|
96
|
+
def include?(*args)
|
97
|
+
@data.include?(*args) or @schema.include?(*args)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns the keys, both set in the construct and specified in the schema.
|
101
|
+
def keys
|
102
|
+
@data.keys | @schema.keys
|
103
|
+
end
|
104
|
+
|
105
|
+
def load(str)
|
106
|
+
end
|
107
|
+
|
108
|
+
def method_missing(meth, *args)
|
109
|
+
meth_s = meth.to_s
|
110
|
+
if meth_s[-1..-1] == '='
|
111
|
+
# Assignment
|
112
|
+
if args.size != 1
|
113
|
+
raise ArgumentError.new("#{meth} takes exactly one argument")
|
114
|
+
end
|
115
|
+
|
116
|
+
self[meth_s[0..-2]] = args[0]
|
117
|
+
elsif include? meth
|
118
|
+
self[meth]
|
119
|
+
else
|
120
|
+
raise NoMethodError.new('no such key in construct')
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Dumps the data (not the schema!) of this construct to YAML. Keys are
|
125
|
+
# expressed as strings.
|
126
|
+
def to_yaml(opts = {})
|
127
|
+
YAML::quick_emit(self, opts) do |out|
|
128
|
+
out.map(taguri, to_yaml_style) do |map|
|
129
|
+
@data.each do |key, value|
|
130
|
+
map.add(key.to_s, value)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: construct
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kyle Kingsbury
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-12 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: aphyr@aphyr.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- lib/construct.rb
|
26
|
+
- LICENSE
|
27
|
+
- README
|
28
|
+
has_rdoc: true
|
29
|
+
homepage: http://aphyr.com
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 1.8.5
|
40
|
+
version:
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
version:
|
47
|
+
requirements: []
|
48
|
+
|
49
|
+
rubyforge_project: construct
|
50
|
+
rubygems_version: 1.3.1
|
51
|
+
signing_key:
|
52
|
+
specification_version: 2
|
53
|
+
summary: Extensible, persistent, structured configuration for Ruby.
|
54
|
+
test_files: []
|
55
|
+
|