construct 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|